4

TaskCompletionSourceで公開されているタスクが呼び出されない場合、どのようにして誰かがタスクを待つまで、結果の計算を決めることができますか?遅延タスクコンプリートソースを実装するにはどうしたらいいですか?

たとえば、次のWaitOneAsync関数を使用してManualResetEventが通知されるまで、他の非同期スレッドの実行をブロックしたいとします。 WaitHandleが通知されたときに発生するThreadPool.RegisterWaitForSingleObjectのコールバックでTaskCompleationSourceを完了します。しかし、誰もタスクを待っていなければ、私はRegisterWaitForSingleObject(WaitHandleが通知された後にタスクが待たれている場合はRegisterWaitForSingleObjectにしたくありません)をしたくありません。

WaitOneAsyncを変更して、RegisterWaitForSingleObjectに対して結果を計算する作業が、誰かがTaskCompleationSource.Taskを待ってからだけ行われるようにするにはどうすればよいですか?

私は...スコット・チェンバレンによって、ここで説明したようImplement AsyncManualResetEvent using Lazy<T> to determine if the task has been awaited答えはカスタムTaskAwaiterにあるのかもしれないが、私は非常に私の解決策に彼の例から得ることができないと信じて:(

public static async Task<T> WaitOneAsync<T>(this WaitHandle waitHandle, Func<T> result) { 

    var tcs = new TaskCompletionSource<T>(); 

    RegisteredWaitHandle rwh = null; 
    rwh = ThreadPool.RegisterWaitForSingleObject(
     waitObject: waitHandle, 
     callBack: (s, t) => { 
      rwh.Unregister(null); 
      tcs.TrySetResult(result()); 
     }, 
     state: null, 
     millisecondsTimeOutInterval: -1, 
     executeOnlyOnce: true 
    ); 

    return await tcs.Task; 
} 

答えて

4

usrが言ったように、Taskawaitであるという反応で何かをすることはできません。しかし、あなたが習慣的なものを使っても大丈夫なら、あなたはできます。

それを行う簡単な方法は、AsyncLazy from Stephen Cleary's AsyncEx使用することです:私は何かを明らかに不足していないよので、TPLは、箱から出して、これを提供していません知って良い

private static Task<T> WaitOneAsyncImpl<T>(WaitHandle waitHandle, Func<T> result) 
{ 
    if (waitHandle.WaitOne(0)) 
     return Task.FromResult(result()); 

    var tcs = new TaskCompletionSource<T>(); 

    RegisteredWaitHandle rwh = null; 
    rwh = ThreadPool.RegisterWaitForSingleObject(
     waitObject: waitHandle, 
     callBack: (s, t) => 
     { 
      rwh.Unregister(null); 
      tcs.TrySetResult(result()); 
     }, 
     state: null, 
     millisecondsTimeOutInterval: -1, 
     executeOnlyOnce: true 
    ); 

    return tcs.Task; 
} 

public static AsyncLazy<T> WaitOneAsync<T>(this WaitHandle waitHandle, Func<T> result) 
    => new AsyncLazy<T>(() => WaitOneAsyncImpl(waitHandle, result)); 
+1

@ChristopherKing ['AsyncFactory.FromWaitHandle'](http://dotnetapis.com/pkg/Nito.AsyncEx/3.0.1/net45/doc/Nito.AsyncEx.AsyncFactory/FromWaitHandle(System.Threading.WaitHandle)も参照してください) ) –

1

これは不可能です

したがって、必要なタスクだけが実際に生成されるように、APIを構造化する必要があります。このように、TPLはイベントやコールバックを提供しません。これはどうですか?

public static Func<Task<T>> CreateWaitOneAsyncFactory<T>(this WaitHandle waitHandle, Func<T> result) { 
return() => WaitOneAsync(waitHandle, result); 
} 

これは、タスクではなくタスクファクトリを返します。これは不正行為ですが、唯一可能な解決策は、この種の不正行為を含むものです。

あなたもカスタムを待つことができます。しかし、それはタスクをまったく伴わず、タスクの合成可能性の機能が失われてしまいます。 Awaitablesは主にC#のコンセプトです。これらを公開すると、不正なAPIになる可能性があります。あなたの質問に関係のない


:あなたはawait tcs.Taskを削除し、直接そのタスクを返すことができます。また、result機能は必要ありません。結果のないTaskを返します。呼び出し元は、必要に応じて結果を追加できます。これにより、APIはWaitOneAsyncクリーナーになります。

+0

をusrと、。この機能を追加するためにTPLを拡張する方法はありますか?スコットがICriticalNotifyCompletionを使用しているように見えます。[安全ではありません] OnCompletedで、継続アタッチイベントをインターセプトします。もしそうなら、私は、これらの文書化されていないTPL配管APIの経験を持つ誰かが、これが価値ある一般的なシナリオだと思って解決策を作ることができると期待していました.. –

+0

彼のしていることは完全に文書化されていて、彼は習慣を待っている。それはもはや仕事ではありません。あなたはこのことを待つことができますが、タスクAPIと共に使用することはできません。 – usr

+0

Ah。さて、それが私の仕事を仕事に変える方法を理解できなかったのです!私は 'Lazy >'を使って1つのTaskCompletionSourceが確実に作成されるようにする作業をオフロードする以外は、あなたが提案したものに行きます。ありがとう! –

関連する問題