2012-09-21 7 views
12

シングルトンクラスは、シングルトンクラスを構築しています。問題は、このデータをロードするにはasyncメソッドを呼び出す必要がありますが、コンストラクタはasyncにはできません。言い換えれば非同期呼び出しを必要とするシングルトンクラス

、私のクラスには、以下の構造を有している。

public class Singleton 
{ 
    private static Singleton instance; 

    private Singleton() 
    { 
     LoadData(); 
    } 

    public static Singleton Instance 
    { 
     get 
     { 
     if (instance == null) 
     { 
      instance = new Singleton(); 
     } 
     return instance; 
     } 
    } 
} 

LoadData()async機能の多くと同様に初期化を呼び出すasync機能です。 LoadData()はどのようにして正しく呼び出すことができますか?

+2

。 –

+0

コンストラクタからasyncメソッドを呼び出すこともできます。 – DarthVader

+0

あなたは何をしたいのかについてより多くの文脈を与えてください。現時点ではやや不明です。 –

答えて

10

このデータをロードするには、非同期メソッドを呼び出す必要がありますが、コンストラクタを非同期にすることはできません。

あなたは、コンストラクタ自体が非同期にすることはできませんが、コンストラクタ内からの非同期メソッドを呼び出すことができます。すぐに結果を戻すことはできません。

TaskまたはTask<T>が返された場合は、非同期操作が完了した時点で常にタスクを継続してクラス内のデータを設定したり、結果をブロックすることができますあなたのシナリオこのオブジェクトの構築の要件を知らなければ、このシナリオで何が適切かを知ることは困難です。


編集:

一つの選択肢、上記の目標を与え、Instanceを取得するメソッドは、メソッド、プロパティではないになるように、あなたのSingleton宣言を変更することです。これは、あなたがそれを非同期で行うことができます:

public class Singleton 
{ 
    private static Singleton instance; 

    private Singleton() 
    { 
      // Don't load the data here - will be called separately 
    } 

    public static async Task<Singleton> GetInstance() 
    { 
     if (instance == null) 
     { 
      instance = new Singleton(); 
      await instance.LoadData(); 
     } 

     return instance; 
    } 
} 

これは、あなたが実際にインスタンスを取得するための呼び出しにawaitを使用することができるようになります。これについての良い点は、非同期操作を呼び出すことが非常にはっきりしており、結果が他の非同期メソッドと同じように戻ってくるため、結果の適切な処理が得られることです。

しかし、これはスレッドセーフではありません(オリジナルはどちらでもありません)ので、複数のスレッドからこのシングルトンを使用する場合は、全体のデザインを再考する必要があります。

もう1つの方法は、Singletonクラスが自動的にデータを読み込まないようにすることです。代わりに、非同期クラスからデータを取得するメソッドを作成します。これはいくつかの本当の利点を提供します。使い方はおそらくもう少し標準的です。複数のスレッドからの呼び出しを(データ読み込みプロセスを制御できるので)簡単にサポートすることができます。非同期クラスインスタンスへのアクセス。

+0

おかげさまで、私に簡単な例を教えていただけますか? – MBZ

+0

@MBZ編集して1つのオプションを表示します。 –

+0

double-check-lockingを使用している場合は、lock()の内部で待機することができないため、問題が発生します。 –

4

あなたはこのためにasynchronous lazy initializationを使用することができます。

public class Singleton 
{ 
    private static readonly AsyncLazy<Singleton> instance = 
     new AsyncLazy<Singleton>(CreateAndLoadData); 

    private Singleton() 
    { 
    } 

    // This method could also be an async lambda passed to the AsyncLazy constructor. 
    private static async Task<Singleton> CreateAndLoadData() 
    { 
    var ret = new Singleton(); 
    await ret.LoadDataAsync(); 
    return ret; 
    } 

    public static AsyncLazy<Singleton> Instance 
    { 
    get { return instance; } 
    } 
} 

そして、あなたはこのようにそれを使用することができます:

Singleton singleton = await Singleton.Instance; 

AsyncLazy<T>を使用する利点の1つは、それがスレッドセーフであるということです。ただし、常にそのデリゲートをスレッドプールスレッドで実行することに注意してください。

1

シングルトンを非同期で初期化することは、あまり意味がありません。あなたは、単にあなたの初期化でタスクを返すメソッドを呼び出したい場合は、単に行うことができます。

var task = MyAsyncMethod(); 
task.Wait(); 
return task.Result; 

方法asyncをする必要はありません。

しかし、シングルトン値がタスクであるために何をしたいことであれば、次のような怠け者を使用することができます。また

Lazy<Task<int>> l = new Lazy<Task<int>>(async() => { int i = await calculateNumber(); return i; }); 

は、Lazy<T>は「シングルトン」を実施するための好ましい方法です。シングルトンクラスは、我々は唯一の内部メカニズムを許可すれば、実際に超簡単です

6

スレッドセーフ、非同期シングルトンためのソリューション...(右維持するかハード)権利を取得するのは難しいです私たちのためにTaskクラスが働きます!

したがって、Taskはどのように機能しますか?たとえば、のインスタンスTask<T>で、awaitのインスタンスが1つあるとします。これでタスクが実行され、Tの値が生成され、返されます。どうすればいいですかawait同じのタスクのインスタンスが再びありますか?この場合、タスクは直前に生成された値を完全に同期してただちに返します。

await同じタスクインスタンス複数のスレッド(通常は競合状態になる)から同時にを取得するとどうなりますか?まず最初に(になります)、タスクコードが実行され、他の人は結果が処理されるまで待機します。結果が生成されると、awaitはすべて(仮想的に)同時に終了し、値を返します。

ので、スレッドセーフであるasyncシングルトンのためのソリューションは、実際には、超簡単です:

public class Singleton 
{ 
    private static readonly Task<Singleton> _getInstanceTask = CreateSingleton(); 

    public static Task<Singleton> Instance 
    { 
     get { return _getInstanceTask; } 
    } 

    private Singleton(SomeData someData) 
    { 
     SomeData = someData; 
    } 

    public SomeData SomeData { get; private set; } 

    private static async Task<Singleton> CreateSingleton() 
    { 
     SomeData someData = await LoadData(); 
     return new Singleton(someData); 
    } 
} 

今、あなたはシングルトンをこのようにアクセスすることができます。

Singleton mySingleton = await Singleton.Instance; 

または

Singleton mySingleton = Singleton.Instance.Result; 

または

SomeData mySingletonData = (await Singleton.Instance).SomeData; 

または

SomeData mySingletonData = Singleton.Instance.Result.SomeData; 

もっとここで読む:あなたが欲しいものを行う必要がありレイジー を使用して Async singleton initialization

+0

非常に洗練されたソリューションです。信じられないほどシンプル:) – TreeAndLeaf