2011-07-29 41 views
1

我有一個我想通過創建一個靜態實例來作爲單例使用的類。當然,我也希望它是線程安全的。通過使用私有靜態方法使單體線程安全嗎?

假設我沒有共享任何私人數據。但是如果我沒有弄錯,仍然存在一個問題,即在調用靜態對象實例的方法時,方法內的變量會在線程間共享,並且會產生不可預知的結果。

但是,調用真正的靜態方法時,將創建一個新的堆棧幀,所以它是線程安全的(你們自己)呢。再次,如果我沒有錯。

請問這個模式單身是線程安全的?

class Singleton 
{ 
    public object SomeMethod(object arg) { 
     return Singleton.SomeMethodImpl(arg); 

    } 
    private static object SomeMethodImpl(object arg) { 
     // is in unique stack frame? 
     object Result; 
     ... 
     return Result; 
    } 
} 

如果你想知道爲什麼我不只是擺在首位創建一個靜態類 - 我需要有一個基於在接口上單,並有不同的實現,作爲戰略的一部分模式。這對靜態類不起作用。

+0

我會看看:http://www.dofactory.com/Patterns/PatternSingleton.aspx如果你想要一個關於如何創建一個單例的一個很好的例子。 –

+0

只是撇去它。良好的信息 - 書籤。對於我的目的,我不認爲我需要擔心鎖定,因爲我沒有訪問類中的任何共享數據。 –

回答

5

只要你的方法是不是從一個實例方法或全局範圍變量得到狀態的方法是可重入和線程安全的。它不一定是靜態的。因此,像:

int AddTwo(int a, int b) 
{ 
    return a + b; 
} 

這是完全線程安全的,可以稱爲你喜歡。即使在方法內部定義變量也是可以的,只要它們是在方法之間共享的實例變量即可。

的方法如:

string ReverseString(string s) 
{ 
    char[] charArray = s.ToCharArray(); 
    Array.Reverse(charArray); 
    return new string(charArray); 
} 

上述的方法也可重入且線程安全的。

只要你開始添加變量無論是靜態或實例是來自不同的範圍,你開始有與線程安全問題。

class BadExample 
{ 
    private int counter; 

    private void IncrementCounter() 
    { 
     ++counter; 
    } 
} 

在上例中,IncrementCounter()方法不是線程安全的。

+0

但是,如果我在非staic方法中定義任何局部變量,它變得不是線程安全的權利?這是我試圖解決的問題 - 我必須做一些事情,比如從需要私人臨時存儲的緩存中取出東西。 –

+0

它不是局部變量,這是問題所在。它的內容來自哪裏。在上面的例子中,所有的「狀態」都來自方法的參數。在你的情況下,它取決於你的緩存是否線程安全。它如何寫入?它是如何讀取的? –

+0

緩存很好,它可以是'ConcurrentDictionary'或'HttpContext.Cache'。但我的印象是,在一個靜態實例化的類的非靜態方法中定義一個局部變量會讓你陷入困境B/C兩個線程可能同時調用該方法並共享該局部變量的同一個實例。 –

0

如果我理解你的意思,那麼你是正確的。

object Result; // this is on its unique stack frame and is safe so far 
Result = new ... // creating something on the heap that Result points to 
       // still safe because it's the only reference to it 

即使多線程調用它,他們也會在堆上創建不同的新變量,並將它們分配給不同堆棧上的Result。

你是唯一的危險是,如果你有私人領域。

方法中的變量是臨時的,只對該方法可見調用。稍後或併發的方法調用分別重新創建這些變量。

您唯一關心的是靜態或實例字段。那些需要同步。

0

你有上面的代碼是線程安全的,爲的原因您指定。我看到的問題是你沒有實現一個單身人士。

您主要擔心線程安全嗎?如果是這樣,線程安全通常適用於線程之間共享的對象實例。這意味着只要您不跨線程共享正常對象或在課堂級別創建靜態數據,您應該沒問題。

我正在添加一個使用單接口的例子,有和沒有工廠。注意:我沒有運行這個代碼。

public interface ISomething 
{ 
    void Method(); 
} 

public class Class1 : ISomething 
{ 
    public void Method() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class Class2 : ISomething 
{ 
    public void Method() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class Singleton 
{ 
    private static ISomething privateObject; 

    public static ISomething Instance() 
    { 
     lock (privateObject) 
     { 
      if (privateObject == null) 
      { 

       privateObject = new Class1(); 
      } 
     } 

     return privateObject; 
    } 
} 

public class SingletonUsingFactory 
{ 
    private static ISomething privateObject; 

    public static ISomething Instance(int param) 
    { 
     lock (privateObject) 
     { 
      if (privateObject == null) 
      { 
       privateObject = FactoryClass.CreationObject(param); 
      } 
     } 

     return privateObject; 
    } 
} 

public static class FactoryClass 
{ 
    public static ISomething CreationObject(int whatToCreate) 
    { 
     ISomething createdObject; 

     switch (whatToCreate) 
     { 
      case 0: 
       createdObject = new Class1(); 
       break; 
      case 1: 
       createdObject = new Class2(); 
       break; 
      default: 
       throw new Exception(); 
     } 

     return createdObject; 
    } 
} 
+0

這個想法是我可以在另一個靜態類中創建它作爲單例,例如'public static Singleton MySingleton = new Singletion()' –

+0

你可以使用單例模式來讓你的接口獨立實現。創建單個對象時只需使用一個鎖 –

相關問題