2008-09-03 77 views
14

我有一个多个类使用的C#单例类。通过Instance访问Toggle()方法是否线程安全?如果是,通过什么假设,规则等等。如果不是,为什么我该如何解决?线程安全使用单身人士的成员

public class MyClass 
{ 
    private static readonly MyClass instance = new MyClass(); 

    public static MyClass Instance 
    { 
     get { return instance; } 
    } 

    private int value = 0; 

    public int Toggle() 
    { 
     if(value == 0) 
     { 
      value = 1; 
     } 
     else if(value == 1) 
     { 
      value = 0; 
     } 

     return value; 
    } 
} 
+0

它需要私人ctor真正的单身? – 2015-08-26 13:28:12

回答

26

通过'Instance'访问'Toggle()'类线程安全吗?如果是的话,通过什么假设,规则等等。如果不是,为什么以及如何修复它?

不,它不是线程安全的。

基本上,两个线程可以运行在同一时间的Toggle功能,所以这可能发生

// thread 1 is running this code 
    if(value == 0) 
    { 
     value = 1; 
     // RIGHT NOW, thread 2 steps in. 
     // It sees value as 1, so runs the other branch, and changes it to 0 
     // This causes your method to return 0 even though you actually want 1 
    } 
    else if(value == 1) 
    { 
     value = 0; 
    } 
    return value; 

你需要用下面的假设来操作。

如果2个线程正在运行,他们可以并将在任何点随机交互和交互。您可以通过写入或读取64位整数或浮点数(在32位CPU上)并且另一个线程可以跳入并将其从底下更改为半途。

如果2个线程永远不会访问任何共同的东西,这并不重要,但只要他们这么做,就需要阻止他们踩到彼此的脚趾。在.NET中这样做的方式是使用锁。

你可以决定什么,在哪里被考虑这样的事情锁定:

对于给定的代码块,如果something的价值得到了超越我改出来的,会很重要?如果是这样,您需要在代码的持续时间内锁定something

在您的例子再次

// we read value here 
    if(value == 0) 
    { 
     value = 1; 
    } 
    else if(value == 1) 
    { 
     value = 0; 
    } 
    // and we return it here 
    return value; 

展望为了使该回什么,我们期待它,我们假设value不会得到读取和return之间变化。为了使这个假设实际上正确,您需要在该代码块的持续时间内锁定value

所以,你可以这样做:

lock(value) 
{ 
    if(value == 0) 
    ... // all your code here 
    return value; 
} 

无论其

在.NET中你只能锁定引用类型。 Int32是一个值类型,所以我们不能锁定它。
我们通过引入'虚拟'对象来解决这个问题,并锁定,无论我们想锁定'值'。

这就是Ben Scheirman所指的。

0

报价:

if(value == 0) { value = 1; } 
if(value == 1) { value = 0; } 
return value; 

value将始终为0 ...

2

你的线程可以在方法和控制转移到一个不同的线程的中间停止。你需要周围代码的临界段...

private static object _lockDummy = new object(); 


... 

lock(_lockDummy) 
{ 
    //do stuff 
} 
+0

,而不是 使用(_lockDummy()) 你的意思是 锁(_lockDummy) – 2009-11-19 01:25:20

+0

谢谢,它现在已修复。 – 2009-11-19 22:15:12

0

好吧,其实我不知道C#那么好......但我确定在Java,所以我会给出的答案,并希望这两者足够相似以至于它会有用。如果没有,我很抱歉。

答案是,不,这不安全。一个线程可以与另一个线程同时调用Toggle(),虽然不太可能使用此代码,但Thread1可以在Thread2检查它的时间和它设置它的时间之间设置value

要修复,只需制作Toggle()​​即可。它不会阻塞任何东西或调用任何可能产生可能调用Toggle()的另一个线程的东西,因此这就是您必须保存的所有内容。

7

原来impplementation不是线程安全的,因为奔指出

一个简单的方法,使线程安全是引入锁定声明。例如。像这样:

public class MyClass 
{ 
    private Object thisLock = new Object(); 
    private static readonly MyClass instance = new MyClass(); 
    public static MyClass Instance 
    { 
     get { return instance; } 
    } 
    private Int32 value = 0; 
    public Int32 Toggle() 
    { 
     lock(thisLock) 
     { 
      if(value == 0) 
      { 
       value = 1; 
      } 
      else if(value == 1) 
      { 
       value = 0; 
      } 
      return value; 
     } 
    } 
} 
1

我也想补充一个受保护的构造函数MyClass的,以防止编译器生成一个公共默认构造函数。

3

这就是我的想法。但是,我是 寻找细节......'Toggle()' 不是一个静态方法,但它是一个静态属性的 成员(当使用'Instance'时 )。这是什么使 它在线程之间共享?

如果您的应用程序是多线程的,并且您可以预见多个线程将访问该方法,那么会使它在线程之间共享。因为你的类是一个单例,你知道不同的线程将访问SAME对象,所以要注意你方法的线程安全性。

而这一般如何适用于单身人士 。我需要在我的课堂上的每种方法中解决 这个问题吗?

正如我上面所说,因为它是一个单例,你知道不同的线程将会访问同一个对象,可能在同一时间。这并不意味着你必须让每个方法获得锁定。如果您发现一个simultaneos调用会导致类的损坏状态,那么你应该应用@Thomas

提到的方法
2

我可以假设的是,单例模式暴露出我的,否则可爱的线程安全类所有常规静态成员的线程问题?

不可以。您的课程根本不是线程安全的。单身人士与此无关。

(我得到我的头周围的事实,呼吁静态对象事业线程问题实例成员)

没什么要么做到这一点。

你必须这样想:在我的程序中是否可以让2(或更多)线程同时访问这段数据?

事实上,您通过单例或静态变量获取数据,或将对象作为方法参数传入并不重要。在一天结束时,你的电脑的RAM中只有一些位和字节,重要的是多个线程是否可以看到相同的位。

1

我在想,如果我转储单例模式并强制每个人都得到该类的新实例,它会缓解一些问题......但这并不阻止其他人初始化该静态对象键入并传递它们......或者从多个线程中分离出来,全部访问来自同一实例的“Toggle()”。

宾果:-)

我现在明白了。这是一个艰难的世界。我希望我没有重构遗留代码:(

不幸的是,多线程是硬,你必须对事情:-) 在这种情况下,最简单的办法是坚持使用单很偏执,并添加围绕价值锁定,就像在例子中一样。