5

次のコードを検討してください。

public Task SomeAsyncMethod()
{
    var tcs = new TaskCompletionSource();
    ... do something, NOT setting the TaskCompletionSource...

    return tcs.Task
}

public void Callsite1()
{
    await SomeAsyncMethod();
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}

public void Callsite2()
{
    SomeAsyncMethod().ContinueWith((task) =>
    {
        Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    });
}

ある時点で、SomeAsyncMethodで作成されたTaskCompletionSourceがThreadPoolスレッドに設定されます。

Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
tcs.SetResult();

TaskCompletionSourceからのタスクが待機されると(Callsite1のように)、継続はSetResultを呼び出したスレッドで同期的に実行されます。(Callsite2のように)ContinueWithが呼び出されると、継続は別のスレッドで非同期に実行されます。

のように、configureawaitを呼び出すことは役に立ちません

public void Callsite1()
{
    await SomeAsyncMethod().ConfigureAwait(true or false);
}

そして、これはこの質問のポイントでさえありません。SomeAsyncMethodの実装者として、SetResultを呼び出して未知の可能性のあるコードを呼び出したくありません。継続を常に非同期でスケジュールしたい。そして、私はawaitを適切に設定するために発信者に頼ることはできません(それがうまくいく場合でも)。

TaskCompletionSourceを構成して、タスクが待機中に同期的に継続を実行しないようにするにはどうすればよいですか?

4

3 に答える 3

12

同期タスクの継続を防ぐ方法はありません。通常、これは問題ではありません。

ただし、これが必要な場合もあります。たとえば、ロックを保持したままタスクを完了している場合などです。そのような場合は、次Task.Runのようにタスクを完了するだけで済みます。

// Set the result on a threadpool thread, so any synchronous continuations
//  will execute in the background.
Task.Run(() => tcs.TrySetResult(result));

// Wait for the TCS task to complete; note that the continuations
//  may not be complete.
tcs.Task.Wait();

これは高度な技術です。これは、私のブログで説明されているガイドライン「コードをブロックしないでください(ずっと下まで)」asyncasyncの例外です。

これは、拡張メソッドとして、私のAsyncExライブラリの一部です。

public static void TrySetResultWithBackgroundContinuations<TResult>(
    this TaskCompletionSource<TResult> @this, TResult result);

この手法は、StephenToubによって彼のブログで最初に公開されました

于 2012-10-02T17:00:45.600 に答える
2

TaskCompletionSourceを構成して、タスクが待機中に同期的に継続を実行しないようにするにはどうすればよいですか?

ありえない。あなたは公開してTaskいるので、一度公開すると、誰でも自由に同期継続をアタッチできます(別のオーバーロードを使用するだけで、 async / awaitを使用する必要ContinueWithはありません)。

于 2012-10-02T15:39:05.827 に答える
2

.NET 4.6の時点で、TaskCreationOption.RunContinuationsAsynchronouslyこの種類またはケースのために追加されました。

[強制]現在のタスクに追加された継続が非同期で実行されます。

したがって、TaskCompletionSourcewithTaskCreationOptions.RunContinuationsAsynchronouslyを作成し、それを呼び出すと.SetResult()ランダムコードが同期的に実行されないようにすることができます。

var tcs = new TaskCompletionSource<bool>(
    TaskCreationOptions.RunContinuationsAsynchronously);

// some time later, somewhere else
tcs.SetResult(true);
DoMoreStuffWithoutWorryingThatSetResultJustRanRandomCode();

詳細については、StephenToubのブログ投稿「.NET4.6の新しいタスクAPI」を参照してください。

このオプションで作成されたタスクが完了すると、継続を同期的に呼び出そうとはしません...可能であれば同期的に実行するように要求されていないかのように、すべての継続を非同期的に呼び出すだけです。

TaskContinuationOptions.RunContinuationsAsynchronouslyのようなものに対して同様の動作が必要な場合もあります.ContinueWith()

[TaskContinuationOptions.RunContinuationsAsynchronously指定]継続タスクを非同期で実行する必要があること。このオプションは、TaskContinuationOptions.ExecuteSynchronouslyよりも優先されます。

于 2016-05-27T20:52:48.690 に答える