C#:如果从多个线程调用静态方法怎么办?


93

在我的应用程序中,我有一个静态方法,该方法同时从多个线程中调用。我的数据有混淆的危险吗?

在我的第一次尝试中,该方法不是静态的,并且我正在创建该类的多个实例。在这种情况下,我的数据会以某种方式混淆。我不确定这是怎么发生的,因为它只是偶尔发生。我还在调试。但是现在该方法是静态的,到目前为止我还没有问题。也许只是运气。我不确定


Answers:


96

在方法内部声明的变量(“ 捕获的 ”变量可能例外)是隔离的,因此您不会遇到任何固有的问题。但是,如果您的静态方法访问任何共享状态,则所有选择均关闭。

共享状态的示例为:

  • 静态场
  • 从公共缓存访问的对象(非序列化)
  • 通过输入参数(以及那些对象的状态)获得的数据,如果多个线程可能正在触摸同一个对象

如果您拥有共享状态,则必须:

  • 请注意不要在共享状态后对其进行更改(更好:使用不可变的对象来表示状态,并将该状态的快照记录到局部变量中-即,不是whatever.SomeData重复引用,而是whatever.SomeData 一次读取到局部变量中,然后只需使用变量-请注意,这仅有助于不可变状态!)
  • 同步访问数据(所有线程必须同步)-互斥或(更精细的)读取器/写入器

1
@Diego-该评论是给我还是给@Holli?
马克·格雷韦尔

对于Holli,只需在您的回复中添加一些实用信息即可。
Diego Pereyra 2010年

1
@Marc我不能完全同意“方法内部声明的变量(可能是“捕获的”变量的例外)是隔离的”。考虑在静态方法中声明的文件句柄。然后一个线程可以在其他线程使用该句柄时访问该句柄。这将导致意外行为。还是您的“捕获”变量也意味着“文件句柄”。
prabhakaran 2012年

9
@prabhakaran如果文件句柄是方法变量,则仅作用于该调用者。任何其他呼叫者都将与其他变量通话(方法变量是每次呼叫)。现在,访问基础文件是一个单独的问题,但是与c#或.NET无关。如果不共享该句柄,则在这种情况下,可能会期望某种互斥锁。
马克·格雷夫

28

是的,这只是运气。;)

方法是否为静态无关紧要,数据是否为静态无关紧要。

如果每个线程都有其自己的类的单独实例以及自己的数据集,则不会有数据混淆的风险。如果数据是静态的,则只有一组数据,并且所有线程共享相同的数据,因此没有办法不将其混淆。

当您在不同实例中的数据仍然混杂在一起时,最有可能是因为数据并不是真正分开的。


6
喜欢那条线- It doesn't matter if the method is static or not, what matters is if the data is static or not。只是要补充一点,在静态方法范围内声明的局部变量不会形成我们在给定场景中需要困扰的那部分数据。
RBT

好答案。帮助很大。
分形

15

静态方法应该适合多个线程。

另一方面,静态数据可能会引起问题,因为需要控制从不同线程访问相同数据的尝试,以确保一次仅一个线程正在读取或写入数据。


2
此处的关键字是同步:-)
G. Stoynev 2013年

2
可以同时进行阅读,但是同时进行读写会导致意外的行为
Freestyle076

9

MSDN总是说:

此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。

编辑:正如这里的人所说,并非总是如此,显然这适用于BCL中以这种方式设计的类,不适用于用户创建的不适用的类。


3
!最终,我了解了该注释的含义,该注释在MSDN文档中经常出现。因此,基本上,当MS在BCL中设计静态方法(在此注释发布的地方)时,它们不会访问该方法范围之外的任何变量/成员/状态。他们完全依赖于方法范围的局部变量,仅用于实现该方法的逻辑。很高兴与您分享。
RBT

@Marcote,不是相反吗?实例成员很安全,因为每个实例只有一个。但是,静态成员不是线程安全的,因为它们在该类的所有实例之间共享吗?quora.com/…–
分形

1
这取决于。默认情况下,我永远不会将实例成员视为安全的。这就是为什么要使用一整套库并避免数据损坏等原因。
Marcote

1
好的,谢谢@Marcote。我正在慢慢弄清楚我有很多东西要学。
分形
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.