锁定操作不起作用

本文关键字:不起作用 操作 锁定 | 更新日期: 2023-09-27 18:29:05

我遇到了一些锁定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

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是来自同一对象的同一方法,所以引用不应该是相同的吗?

如有任何帮助,我们将不胜感激。谢谢

锁定操作不起作用

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

此行:

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);
};
...

当您多次将一个方法强制转换为Action时(当您传递settings.SetANumber时),并不意味着您得到相同的Action对象。您会得到多个执行相同操作的Action对象。

试试这个:

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

这样可以确保只有一个Action对象。