锁定操作不起作用

时间:2016-01-05 14:39:20

标签: c# multithreading locking

我遇到了一些锁定Action并且发现它不起作用的代码。这是代码的(简化和愚蠢)版本:

class Program
{
    static void Main(string[] args)
    {
        var settings = new Settings(0);
        Action<int> threadAction = i =>
        {
            BusyBody.DoSomething(settings.GetANumber, settings.SetANumber, i);
        };
        ThreadPool.QueueUserWorkItem(delegate { threadAction(1); });
        ThreadPool.QueueUserWorkItem(delegate { threadAction(2); });
        Console.ReadLine();
    }

    class Settings
    {
        int i;

        public Settings(int i)
        {
            this.i = i;
        }

        public int GetANumber() => i;

        public void SetANumber(int i) => this.i = i;
    }

    class BusyBody
    {
        public static void DoSomething(Func<int> getInt, Action<int> setInt, int i)
        {
            lock (setInt)
            {
                Console.WriteLine("Getting int: " + getInt());
                Console.WriteLine("i " + i);
                setInt(i);
                Console.WriteLine("set");
            }
        }
    }
}

我希望这会产生以下输出:

Getting int: 0
i 1
set
Getting int: 1
i 2
set

OR

Getting int: 0
i 2
set
Getting int: 2
i 1
set

取决于首先通过lock的任何线程。然而,这不是我所看到的。相反,我看到了:

Getting int: 0
i 2
Getting int: 0
i 1
set
set

看起来两个线程都进入lock。为什么会这样?被锁定的Action是来自同一对象的相同方法,所以引用不应该相同吗?

任何帮助将不胜感激。谢谢!

2 个答案:

答案 0 :(得分:1)

这里的问题是你要锁定两个不同的对象。

这一行:

BusyBody.DoSomething(settings.GetANumber, settings.SetANumber, i);

是这个的缩写:

BusyBody.DoSomething(new Func<int>(settings.GetANumber), new Action<int>(settings.SetANumber), i);

两个new Func<int>(...)new Action<int>(...)表达式将在每次调用时生成新的委托对象,因此您不会锁定同一个对象实例。

如果您想共享这些对象,则必须创建一次:

...
Func<int> getANumber = settings.GetANumber;
Action<int> setANumber = settings.SetANumber;
Action<int> threadAction = i =>
{
    BusyBody.DoSomething(getANumber, setANumber, i);
};
...

答案 1 :(得分:0)

当您将方法多次转换为Action时(当您传递settings.SetANumber时),这并不意味着您获得了相同的Action对象。你得到多个 Action对象做同样的事情。

试试这个:

 Action<int> set_action = settings.SetANumber;

 Action<int> threadAction = i =>
 {
     BusyBody.DoSomething(settings.GetANumber, set_action, i);
 };

这可确保您拥有一个Action对象。