16

その構築に関するいくつかのデータをロードするシングルトンクラスがあります。問題は、このデータをロードするには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()

4

4 に答える 4

25

クラスの内部メカニズムのみを機能させる場合、スレッドセーフな非同期シングルトンのソリューションは実際には非常に単純です。Task

それで、どのように機能しTaskますか?あなたがaのインスタンスを持っていて、それを一度持っているとしましょう。これでタスクが実行され、の値が生成されて返されます。同じタスクインスタンスをもう一度使用した場合はどうなりますか?この場合、タスクは以前に生成された値を完全に同期してすぐに返します。Task<T>awaitTawait

また、複数のスレッドから同時に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;

詳細はこちら:非同期シングルトン初期化

于 2015-11-22T12:32:41.930 に答える
14

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

コンストラクター自体を非同期にすることはできませんが、コンストラクター内から非同期メソッドを呼び出すことができます。結果がすぐに返されるわけではありません。

非同期メソッドがTaskまたはTask<T>を返す場合、シナリオで最も意味のあるものに応じて、非同期操作が完了したら、いつでもタスクの継続を使用してクラス内にデータを設定するか、結果をブロックすることができます。このオブジェクトの構築の要件を知らなければ、このシナリオで何が適切かを知ることは困難です。


編集:

上記の目標を前提とした1つのオプションは、Singleton宣言を変更して、を取得するInstanceメソッドがプロパティではなくメソッドになるようにすることです。これにより、非同期にすることができます。

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クラスがデータを自動的にロードしないようにすることです。代わりに、クラスからデータを取得するメソッドを非同期にします。これは、使用法がおそらくもう少し標準的であり、複数のスレッドからの呼び出しを、作成して処理するよりも少し簡単に(データの読み込みプロセスを制御できるため)サポートできるため、いくつかの実際の利点を提供します。クラスインスタンスへの非同期アクセス。

于 2012-09-21T16:52:20.787 に答える
9

これには、非同期レイジー初期化を使用できます。

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;

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

于 2012-09-21T17:25:47.053 に答える
1

さて、シングルトンを非同期的に初期化することはあまり意味がありません。初期化でTaskを返すメソッドを呼び出したいだけの場合は、次のようにするだけです。

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

メソッドを作成する必要はありませんasync

ただし、シングルトン値をタスクにする必要がある場合は、Lazyを次のように使用できます。

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

さらに、Lazy<T>は「シングルトン」を実装するための推奨される方法です。シングルトンクラスを正しく行うのは難しい(または正しく保つのが難しい)...

于 2012-09-21T17:41:44.733 に答える