まず、Servyは同様の質問への回答にいくつかのコードを記述しました。
/programming/22049339/how-to-create-a-cancellable-task-loop
Servyの回答にはContinueWith()
、async
およびawait
キーワードを明示的に使用せずにTPL構造を使用した同様のループが含まれています。だからあなたの質問に答えるために、ループが展開されたときにあなたのコードがどのように見えるかを考えてくださいContinueWith()
private static Task GetWorkWhileNotQuit()
{
var tcs = new TaskCompletionSource<bool>();
Task previous = Task.FromResult(_quit);
Action<Task> continuation = null;
continuation = t =>
{
if (!_quit)
{
previous = previous.ContinueWith(_ => GetWorkAsync())
.Unwrap()
.ContinueWith(_ => previous.ContinueWith(continuation));
}
else
{
tcs.SetResult(_quit);
}
};
previous.ContinueWith(continuation);
return tcs.Task;
}
これはあなたの頭を包み込むのに少し時間がかかりますが、要約すると:
continuation
「現在の反復」のクロージャーを表す
previous
「前の反復」Task
の状態を含むことを表します(つまり、「反復」がいつ終了し、次の反復の開始に使用されるかを認識します。)
- がを
GetWorkAsync()
返すと仮定するとTask
、つまり、「内部タスク」(つまりの実際の結果)を取得ContinueWith(_ => GetWorkAsync())
するTask<Task>
ための呼び出しが返さUnwrap()
れます。GetWorkAsync()
そう:
- 最初は前の反復がないため、単に値が割り当てられます
Task.FromResult(_quit)
-その状態はとして始まりTask.Completed == true
ます。
continuation
使用して初めて実行されますprevious.ContinueWith(continuation)
- の完了状態を反映するように
continuation
クロージャが更新さprevious
れます_ => GetWorkAsync()
- ときに
_ => GetWorkAsync()
完了すると、それは「と続けて」_previous.ContinueWith(continuation)
-すなわち呼び出しcontinuation
、再びラムダを
- 明らかにこの時点
previous
で、の状態で更新されている_ => GetWorkAsync()
ため、戻りcontinuation
時にラムダが呼び出されGetWorkAsync()
ます。
continuation
ラムダはいつもの状態チェックし_quit
た場合、そうし_quit == false
た後、それ以上の継続が存在しない、とTaskCompletionSource
の値に設定されます_quit
、そして、すべてが完成します。
別のスレッドで実行されている継続に関する観察については、このブログの「タスクは(まだ)スレッドではなく、非同期は並列ではない」ので、async
/ await
キーワードがあなたのために行うことではありません。- https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async-is-not-parallel/
GetWorkAsync()
スレッド化とスレッドの安全性に関して、メソッドを詳細に検討する価値があることをお勧めします。診断により、非同期/待機コードが繰り返された結果、別のスレッドで実行されていることが判明した場合は、そのメソッド内またはそのメソッドに関連する何かが原因で、新しいスレッドが別の場所に作成されている必要があります。(これが予想外の場合、おそらく.ConfigureAwait
どこかにありますか?)