在C#2.0+中,是否有必要锁定另一个线程将执行的闭包?

时间:2009-02-27 23:00:15

标签: c# concurrency

在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)生效。

3 个答案:

答案 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)

你需要两件事:

  1. 赋值应该是原子的(不会被修改相同数据的另一个线程中断)。
  2. 如果线程在另一个线程中被更改,则线程需要获取当前值。

    1. 在int的情况下不需要锁定(参见ECMA 344 C# specification, look for atomic)。

    2. 锁定无法保证
    3. 。使用volatile来确保(见同一文档中的17.4.3)。

  3. 总结:你不需要锁,但你可能需要挥发。

    编辑(参见备注):局部变量上不可能使用volatile(在本例中为x)。在局部变量上,我不知道任何可以保证x的多次读取(在同一个线程中)将获取在其他线程中所做的更改。在提供的代码中只有一个读取,但我想这是一个简化。