我遇到了一些锁定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
是来自同一对象的相同方法,所以引用不应该相同吗?
任何帮助将不胜感激。谢谢!
答案 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
对象。