将来のUIスレッドメッセージループの反復の後までコードの実行を延期する必要がある場合は、次のようにします。
await Task.Factory.StartNew(
() => {
MessageBox.Show("Hello!");
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
これは次のようになります。await Task.Yield(); MessageBox.Show("Hello!");
他にも、必要に応じてタスクをキャンセルすることもできます。
デフォルトの同期コンテキストの場合は、同様に使用できます。await Task.Run
プールスレッドを続行します。
実際、私は好きですTask.Factory.StartNew
そしてTask.Run
より多いTask.Yield
どちらも継続コードの範囲を明示的に定義しているからです。
だから、どんな状況でawait Task.Yield()
本当に便利ですか?
ある状況Task.Yield()
あなたがいるときに実際に便利ですawait
再帰的に呼ばれる同期的に完了Task
の。 csharpのasync
/await
「Zalgoをリリース」可能な場合は継続を同期的に実行することで、完全同期再帰シナリオのスタックは、プロセスが停止するほど大きくなる可能性があります。これはまた、テールコールが原因でサポートされていないことも原因の1つだと思います。Task
間接。await Task.Yield()
インラインではなくスケジューラによって継続が実行されるようにスケジュールします。これにより、スタックの増加を回避し、この問題を回避することができます。
また、Task.Yield()
メソッドの同期部分を短くするために使用できます。呼び出し側が自分のメソッドの情報を受け取る必要がある場合Task
あなたのメソッドが何らかのアクションを実行する前に、あなたは使うことができますTask.Yield()
強制的にTask
そうでなければ自然に起こるよりも早く。たとえば、次のローカルメソッドのシナリオでは、async
メソッドはそれ自身の参照を取得することができますTask
安全に(これを単一同時実行で実行していると仮定してください。SynchronizationContext
winformsやviaなどでニトロAsyncContext.Run()
):
using Nito.AsyncEx;
using System;
using System.Threading.Tasks;
class Program
{
// Use a single-threaded SynchronizationContext similar to winforms/WPF
static void Main(string[] args) => AsyncContext.Run(() => RunAsync());
static async Task RunAsync()
{
Task<Task> task = null;
task = getOwnTaskAsync();
var foundTask = await task;
Console.WriteLine($"{task?.Id} == {foundTask?.Id}: {task == foundTask}");
async Task<Task> getOwnTaskAsync()
{
// Cause this method to return and let the 「task」 local be assigned.
await Task.Yield();
return task;
}
}
}
出力:
3 == 3: True
の同期部分を強制的に短くすることができるという現実のシナリオは考えられないのが残念です。async
方法は何かをするための最良の方法です。先ほど示したようなトリックを実行できることを知っていると便利な場合がありますが、それはより危険にもなりがちです。多くの場合、より良い、より読みやすい、そしてよりスレッドセーフな方法でデータを渡すことができます。たとえば、ローカルメソッドにそれ自身への参照を渡すことができます。Task
を使ってTaskCompletionSource
代わりに:
using System;
using System.Threading.Tasks;
class Program
{
// Fully free-threaded! Works in more environments!
static void Main(string[] args) => RunAsync().Wait();
static async Task RunAsync()
{
var ownTaskSource = new TaskCompletionSource<Task>();
var task = getOwnTaskAsync(ownTaskSource.Task);
ownTaskSource.SetResult(task);
var foundTask = await task;
Console.WriteLine($"{task?.Id} == {foundTask?.Id}: {task == foundTask}");
async Task<Task> getOwnTaskAsync(
Task<Task> ownTaskTask)
{
// This might be clearer.
return await ownTaskTask;
}
}
}
出力:
2 == 2: True
SynchronizationContext
しかし、それと関係があるでしょう。このようなことをしているのであれば、グラフィカルアプリケーションを使用している場合は、GUIスレッドに表示されていないことを願います。ただし、タスクAPIを繰り返し使用している場合は、待っているタスクが完了していないことが確実にわからないため、実行する必要があるかもしれません。あなたはいつでもタスクに条件付きにすることができますTask.IsCompleted
そしてカウンター。 - binki
非同期タスクに値を返させたい場合を考えてください。
既存の同期方法
public int DoSomething()
{
return SomeMethodThatReturnsAnInt();
}
非同期にするには、asyncキーワードを追加して戻り型を変更します。
public async Task<int> DoSomething()
Task.Factory.StartNew()を使用するには、メソッドの1行の本文を次のように変更します。
// start new task
var task = Task<int>.Factory.StartNew(
() => {
return SomeMethodThatReturnsAnInt();
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext() );
// await task, return control to calling method
await task;
// return task result
return task.Result;
使用する場合は1行追加await Task.Yield()
// this returns control to the calling method
await Task.Yield();
// otherwise synchronous method scheduled for async execution by the
// TaskScheduler of the calling thread
return SomeMethodThatReturnsAnInt();
後者ははるかに簡潔で読みやすく、そして実際には既存の方法をそれほど変更しません。
return await Task.Factory.StartNew(() => SomeMethodThatReturnsAnInt(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
。それにもかかわらず、私はポイント+1を見ます。これに関する問題は、議論されている、制御フローのやや直観的でない変更です。ここに。 - noseratioawait task;
ライン - MohoSomeMethodThatReturnsAnInt
にasync
、私は単にすることができます:public Task<int> DoSomething() { return Task.FromResult(SomeMethodThatReturnsAnInt()); }
。または、async
例外伝播の意味:public async Task<int> DoSomething() { return await Task.FromResult(SomeMethodThatReturnsAnInt()); }
。明らかにawait Task.Yield()
ここでは冗長で望ましくないでしょう。投票を元に戻して申し訳ありません。 - noseratio
Task.Yield()
の同期部分の「穴を開ける」には最適です。async
方法。
個人的に私は私が自己相殺を持っている場合にそれが便利だと思ったasync
メソッド(独自の対応を管理するものCancellationTokenSource
極めて短い期間内に(すなわち、相互依存UI要素のイベントハンドラによって)複数回呼び出される可能性がある各後続の呼び出しで前に作成されたインスタンスを取り消す。こんなときに使ってTask.Yield()
続いて、IsCancellationRequested
すぐに確認してくださいCancellationTokenSource
とにかくスワップアウトされると、結果がとにかく捨てられる可能性がある高価な作業をすることを防ぐことができます。
これはSelfCancellingAsyncへの最後のキューに入れられた呼び出しだけが高価な仕事を実行して、完了まで走らせるという例です。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskYieldExample
{
class Program
{
private static CancellationTokenSource CancellationTokenSource;
static void Main(string[] args)
{
SelfCancellingAsync();
SelfCancellingAsync();
SelfCancellingAsync();
Console.ReadLine();
}
private static async void SelfCancellingAsync()
{
Console.WriteLine("SelfCancellingAsync starting.");
var cts = new CancellationTokenSource();
var oldCts = Interlocked.Exchange(ref CancellationTokenSource, cts);
if (oldCts != null)
{
oldCts.Cancel();
}
// Allow quick cancellation.
await Task.Yield();
if (cts.IsCancellationRequested)
{
return;
}
// Do the "meaty" work.
Console.WriteLine("Performing intensive work.");
var answer = await Task
.Delay(TimeSpan.FromSeconds(1))
.ContinueWith(_ => 42, TaskContinuationOptions.ExecuteSynchronously);
if (cts.IsCancellationRequested)
{
return;
}
// Do something with the result.
Console.WriteLine("SelfCancellingAsync completed. Answer: {0}.", answer);
}
}
}
ここでの目標は、同じコード上で同期的に実行されるコードを許可することです。SynchronizationContext
asyncメソッドへの待ち合わせのない呼び出しが戻った直後(最初にヒットしたとき)await
)asyncメソッドの実行に影響する状態を変更します。これは、によって達成されるのとよく似た調整です。Task.Delay
(ここではゼロ以外の遅延期間について説明しています)実際の、潜在的に顕著な遅延、これは状況によっては歓迎されないことがあります。
Task.Yield
またはヘルパーTask.Delay
、私のバージョンはこちら取り消しおよび再始動パターンの。 - noseratioasync void
の署名SelfCancellingAsync
。 - noseratiovoid
なぜなら、それをTask
それは待たれるべきだと示唆していますが、これはここでは当てはまりません。例外をスローするのではなく、キャンセルが検出されたときにメソッドから戻るのもその理由です(一般的には、呼び出し側にキャンセルを通知するための推奨される方法です)。 - Kirill Shlenskiyasync Task
例外を投げて呼び出し側にそれらを処理させることは、物事を進める上で好ましい方法です。の場合async void
ただし、例外処理は通常、非同期メソッド自体の本体に組み込まれる必要があります。 - Kirill ShlenskiySelfCancellingAsync
。ただし、最終的には、外部からのキャンセルを要求されるようになります(例:Main
メソッドは終了し、アプリは終了します。この時点で、あなたはおそらく保留中のタスクを待ちたいと思うでしょう。SelfCancellingAsync
優雅に仕上げるために。これがあなたのアプローチでどのように行われるのかわかりません。 - noseratio
Task.Yield
に代わるものではありませんTask.Factory.StartNew
またはTask.Run
。彼らは全く違います。あなたがawait
Task.Yield
現在のスレッド上の他のコードが、スレッドをブロックせずに実行できるようにします。待っているようにそれを考えるTask.Delay
を除いてTask.Yield
特定の時間を待つのではなく、タスクが完了するまで待ちます。
注:使用しないでくださいTask.Yield
UIスレッドで、UIが常に反応し続けると仮定します。いつもそうとは限りません。
Task.Yield
そして他の習慣の待機者は働きます。この観点から、私はあなたの最後の文以外のすべてにはほとんど同意しません。 - noseratioTask.Yield
特定の時間を待つのではなく、タスクが完了するまで待ちます。」 - 他のタスクが完了するのを待ちません。それはただ "実行準備ができている"継続をスケジュールして、並んで待っている次の既存の "実行準備ができている"継続(それ自体かもしれない)に切り替えるだけです。すなわち、これは単にその時点であなたのタスクにそれが同期的であることをやめることを強いるだけです。 - binki
Task.Yield
単体テストであいまいなASP.NETの問題を回避するにはasync
方法してはいけません同期的に完了する。 - Stephen ClearyMessageBox.Show()
渡すことなくIWin32Window owner
引数ウィンドウにフォーカスがないときにそのコードが実行されると、メッセージボックスがウィンドウの下に飛び出します。これをGUIスレッドで行うと特に混乱します。また、合格した場合IWin32Window
にMessageBox.Show()
あなたはUIスレッドでそうする必要があります。だから、その場合、あなたはしてはいけませんつかいますTask.Run()
そしてしなければならないパスTaskScheduler
にStartNew()
。 - binkiMessageBox.Show()
別のスレッドでMessageBox.Show()
メッセージキューを送り出し、GUIにスケジュールされた継続を実行します。SynchronizationContext
。つまり、フォームの更新と表示を続けることができます。async
通常の方法でも、MessageBox.Show()
見せている。 - binki