2012-03-28 9 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に対するガードは、ユーザーのエッジケースに対するガードがそれを仕事必要としませんイニシャライズを行い、Init()の呼び出しを忘れた人を守ります。

しかし、それは2つの理由でちょっと気難しいと感じています。 a)起動時にInitメソッドに2回移動します。 b)シングルトン内でスレッドを実行したいが、何かが初期化を開始しなければならない。

これを処理するクリーナ/ニース/より良い方法がありますか?

ありがとうございました。

** EDIT:コメントで指摘されているように、Initはプライベートとして誤ってスコープされていました。公開され、訂正されている必要があります。

+1

あなたのシングルトンを初期化するために、 '><'レイジーを使用する必要があります[ジョンスキートで説教として](http://csharpindepth.com/Articles/ General/Singleton.aspx)を使用して、シングルトンが実際に使用されるまで初期化をオフロードします。 –

+1

Jonは記事「私の個人的な好みは解決策4です:」にあります。これはLazy ソリューションではありません。彼の推論と詳細についての記事をお読みください。 –

+0

@MitchWheat - Heh、私は10回以上読んだことがありますが、過去の記事を読んだことはありません。それは私の舌を保持するために私に教えてくれるでしょう(おそらくそうではありません) –

答えて

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メソッドが実行されたときにインスタンスプロパティはできるだけ早く返すことを意味しているトリガーたままになります。私はmybe Task<T>で行くと思います

+0

素晴らしいソリューション!ありがとう!しかし、いくつかの質問:a)QueueUserWorkItemではなくTask.Factory.StartNew()の理由? b)AutoResetEventを使用せずにAutoResetEventを使用するようにするにはどうすればよいですか? – pdalbe01

+0

a)いいえ、個人的な好み。 b)はい。 'AutoResetEvent'はシグナルをリセットし、最初の' Wait'が完了したときにすべての呼び出し元をブロックします。 – jgauffin

+1

わずかな問題:静的コンストラクタは、クラスメンバーが初めて使用されたときにのみ呼び出されます。したがって、これはアプリケーションの起動時にInit()を実行しません。 – pdalbe01

0

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() { } 
    } 
} 
関連する問題