2012-03-28 49 views
6

我目前有一個單例,最初可能需要10秒才能初始化。但是,我不希望我的用戶對此進行初始化處理(等待),所以我寧願在啓動應用程序時在後臺線程上引導此組件。下面是我有:在後臺線程上初始化一個單例的常見模式

辛格爾頓:

public class MySingleton 
{ 
    private static MySingleton _instance; 
    private static readonly object _locker = new object(); 

    private MySingleton() 
    { 
     Init(); 
    } 
    public static MySingleton Instance 
    { 
     if(_instance == null) _instance = new MySingleton(); 
     return _instance; 
    } 
    public void Init() 
    { 
     lock(_locker) 
     { 
      if(_instance != null) return; 

      // long running code here... 
     } 
    } 
} 

應用程序啓動:

Task.Factory.StartNew(() => MySingleton.Instance.Init()); 

此代碼的工作,對雙初始化警衛,對用戶的邊緣情況警衛需要它,它是前完成初始化並防止忘記調用Init()的人。

但是,由於兩個原因,它感覺有點笨拙: a)我將在啓動時兩次進入Init方法。 b)我想在單例內部進行線程化,但是必須啓動初始化。

是否有一個更清潔/更好/更好的方式來處理?

在此先感謝大家的幫助。

**編輯:正如在評論中指出的那樣,Init被誤認爲是私有的。它應該是公開的,並已得到糾正。

+1

您應該使用'懶<>'來初始化單[如宣揚的喬恩斯基特(​​http://csharpindepth.com/Articles/ General/Singleton.aspx)卸載初始化,直到實際使用單例。 – 2012-03-28 04:15:10

+1

Jon在那篇文章中寫道:「我個人的偏好是針對解決方案4:」這不是懶惰的解決方案。請閱讀文章,瞭解他的推理和更多信息。 – 2012-03-28 04:19:39

+0

@MitchWheat - 嘿,我想(在10次以上的閱讀中)我從來沒有閱讀過這些例子的文章。這會教我如何保持我的舌頭(可能不是) – 2012-03-28 04:30:08

回答

0

你應該定義和調用如下單身類...

var instance = MySingleton.Instance; 
    while (true) 
    { 
    /// check for whether singleton initialization complete or not 
    if (MySingleton.Initialized) 
    { 
     break; 
    } 
    } 



    public class MySingleton 
     { 
      private static MySingleton _instance; 
      private static readonly object _locker = new object(); 
      public static bool Initialized { get; set; } 

      private MySingleton() 
      { 
       ThreadPool.QueueUserWorkItem(call => Init()); 
      } 

      public static MySingleton Instance 
      { 
       get 
       { 
        if (_instance == null) 
         _instance = new MySingleton(); 

        return _instance; 
       } 
      } 

      private void Init() 
      { 
       lock (_locker) 
       { 
        if (Initialized) 
         return; 
        // long running code here...   
        for (int i = 0; i < 10000; i++) 
        { 

        } 
        Initialized = true; 
       } 
      } 
     } 
9

使用靜態構造函數來觸發它,並在同步一個ManualResetEvent。它爲您提供了一個解決方案,即在實際課程中完成所有任務。因此,它不依賴於某人應該調用init方法。

public class MySingleton 
{ 
    private static MySingleton _instance; 
    private static ManualResetEvent _initEvent = new ManualResetEvent(false); 

    static MySingleton() 
    { 
     ThreadPool.QueueUserWorkItem(state => Init()); 
    } 

    public static MySingleton Instance 
    { 
     _initEvent.Wait(); 
     return _instance; 
    } 
    private static void Init() 
    { 
     _instance = new MySingleton(); 
     // long running code here... 


     _initEvent.Set(); 
    } 
} 

事件將保持信號一旦被觸發,這意味着會使用實例屬性將盡快返回時,init方法完成。

+0

了不起的解決方案!謝謝!幾個問題雖然:a)任何理由QueueUserWorkItem而不是Task.Factory.StartNew()? b)使用ManualResetEvent而不是AutoResetEvent的任何反例? – pdalbe01 2012-03-28 14:36:19

+0

a)不,個人喜好。 b)是的。當第一個「Wait」完成時,AutoResetEvent將重置信號並阻止所有呼叫者。 – jgauffin 2012-03-28 14:41:55

+1

輕微問題:只有在第一次使用類成員時纔會調用靜態構造函數。所以這不會在應用程序啓動過程中運行Init()。 – pdalbe01 2012-03-28 14:48:47

0

我mybe與Task<T>去:

class Program 
{ 
    static void Main(string[] args) 
    { 
     MySingleton.Init(); 

     Thread.Sleep(7000); 

     Console.WriteLine("Getting instance..."); 
     var mySingleton = MySingleton.Instance; 
     Console.WriteLine("Got instance."); 
    } 

    public class MySingleton 
    { 
     private static Lazy<MySingleton> instance; 

     public static MySingleton Instance 
     { 
      get { return instance.Value; } 
     } 

     public static void Init() 
     { 
      var initTask = Task.Factory.StartNew(() => 
      { 
       for(int i = 0; i < 10; i++) 
       { 
        Thread.Sleep(1000); 
        Console.WriteLine("Doint init stuff {0}...", i); 
       } 

       return new MySingleton(); 
      }); 

      instance = new Lazy<MySingleton>(() => initTask.Result); 
     } 

     private MySingleton() { } 
    } 
}