C#:有关成员变量的线程安全性的问题

时间:2009-09-04 17:34:58

标签: c# multithreading synchronization

有人可以解释为什么线程上的示例总是会使一个对象(特别是一个成员变量)静态,如果它要被多个线程访问?

我的问题是将成员变量设为静态意味着它将在该类的所有其他实例中共享。有时我发现我希望类中的多个线程“触摸”成员变量,但同时允许每个对象拥有自己的副本。

执行我提到的内容的答案是否会取代:

  • 使用volatile关键字
  • 使用锁定(对象)

2 个答案:

答案 0 :(得分:8)

没有什么要求成员变量对于线程是静态的。

大多数示例使用静态变量,特别是因为他们试图展示如何在两个单独的线程之间同步数据。如果您希望这样做,您需要具有两个线程都可访问的值。静态变量是最简单的选项,因为它们随处可见。

您可以轻松地将对类的引用传递到您的线程中,并让该线程和您的主线程共享一个非静态成员变量。但是,您需要提供一个良好的锁定/同步机制,因为您将有两个线程共享一个类的实例。

答案 1 :(得分:3)

如果你希望每个线程使用相同的成员变量,但也保持一个单独的副本,那么你在这里就有了矛盾。要么他们使用相同的变量,要么他们不使用。除非我不理解你。

话虽如此,如果你真的需要一个静态字段来访问多个线程,每个线程都维护自己的私有值,你可以使用ThreadStatic属性。此属性确保静态字段对每个线程都是私有的(通常称为“本地线程”)。

[ThreadStatic]
private static bool s_threadHasDoneItsWork;

请注意,您不能通过静态构造函数初始化线程本地静态字段,也不能直接初始化为static type field = value。 (编译器不会抱怨,但它无法正常工作。)


volatile告诉运行时必须始终直接从主内存访问字段(静态或非静态),因此您不需要使用锁或内存屏障来同步线程和内核。它总是是最新的,可以这么说,而其他字段仍然可以等待处理器的内存缓存与主内存同步。

但这正是它所做的限制:访问该特定字段。一旦你读完它,这个值就会再次失效,所以不要想到像volatileField ++这样的东西(这意味着“读取volatileField,在你刚读过的值中添加一个,设置volatileField”,而不是“增加volatileField”,你需要使用Interlocked类,这要贵得多。

使用volatile字段的一种安全方法是直接读取它们,但是当您修改它们时,在读取或写入它们之前使用锁定机制。 (我能想到的唯一合理的例外是布尔标志,比如“我现在已经完成了”。)

毫不奇怪,挥发性油田的实际应用相当有限。坚持简单锁定; lock关键字和相应的Monitor类的方法同时处理单个用户内存同步(一旦进入和退出锁定)。