在C#2.0+中,是否有必要锁定另一个线程将执行的闭包?具体来说,在下面的示例中,是否需要锁定以确保Maint线程将其x值刷新到共享内存,并且线程t从共享内存中读取x的值?
我认为是,但如果我错了,请参考,例如一篇权威性的(例如,MSDN)文章,说明为什么不这样做。谢谢!
delegate void Foo();
public static void Main()
{
Foo foo = null;
object guard = new object();
int x = 1;
lock (guard)
{
foo = () =>
{
int temp;
lock (guard) temp = x;
Console.WriteLine(temp);
};
}
Thread t = new Thread(() => foo());
t.Start();
t.Join();
}
编辑:澄清我想知道C#2.0+,也就是说.NET 2.0 +更强大的内存模型(比ECMA 335 CLI)生效。
答案 0 :(得分:4)
调用任何构造函数都具有.NET内存模型中的发布语义。 (不是在CLI内存模型中,而是在.NET内存模型中。)因此,通过调用Thread
构造函数 - 以及委托构造函数本身 - 我相信你没事。如果在最终构造函数之后将x设置为1 ,我就不太确定了。 (编辑:构建器业务可能更多地与新的 Java 内存模型相比,而不是.NET,考虑到后面有关写入的内容......)
事实上,我有怀疑 所有写入都具有发布语义(同样,在已实现的.NET内存模型中),但并非所有读取都具有获取语义。换句话说,数据将始终可用到其他线程,但这些线程可能不会使用。在这种情况下,新线程不会“拥有”变量的旧值 - 在它开始之前,它无法在逻辑上读取该值,因此您在这方面是安全的。
我可以查看“所有写入”方面的事情 - 我怀疑Joe Duffy的博客或书中有关于它的内容。
编辑:来自“Windows上的并发编程”P516:
强者的主要差异 2.0+模型是它可以防止商店被重新排序。
我相信这就足够了。我怀疑,Stack Overflow上很少有人能够肯定地说 - 我不会完全信任很多不在MS CLR或并发团队中的人,除了Jeff Richter
答案 1 :(得分:1)
除非多于一个线程可能同时执行它,否则不需要锁定。
答案 2 :(得分:0)
你需要两件事:
如果线程在另一个线程中被更改,则线程需要获取当前值。
在int的情况下不需要锁定(参见ECMA 344 C# specification, look for atomic)。
。使用volatile来确保(见同一文档中的17.4.3)。
总结:你不需要锁,但你可能需要挥发。
编辑(参见备注):局部变量上不可能使用volatile(在本例中为x)。在局部变量上,我不知道任何可以保证x的多次读取(在同一个线程中)将获取在其他线程中所做的更改。在提供的代码中只有一个读取,但我想这是一个简化。