uebu-kaihatsu.jp.net

Task.Yield()はいつ使用しますか?

私はasync/awaitとTaskを頻繁に使用していますが、Task.Yield()を使用したことは一度もないため、正直に言うと、このメソッドが必要な理由がわかりません。

Yield()が必要な場合、誰かが良い例を挙げることができますか?

187
Krumelur

async/awaitを使用する場合、await FooAsync()を実行するときに呼び出すメソッドが実際に非同期で実行されるという保証はありません。内部実装は完全に同期したパスを使用して自由に戻ることができます。

ブロックしないことが重要なAPIを作成していて、一部のコードを非同期で実行し、呼び出されたメソッドが同期的に実行される(実質的にブロックする)可能性がある場合、await Task.Yield()を使用すると、メソッドが強制的に非同期になり、その時点でコントロールを返します。残りのコードは、現在のコンテキストで後で実行されます(この時点でも、同期的に実行される可能性があります)。

これは、いくつかの「長時間実行」初期化を必要とする非同期メソッドを作成する場合にも役立ちます。

 private async void button_Click(object sender, EventArgs e)
 {
      await Task.Yield(); // Make us async right away

      var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later

      await UseDataAsync(data);
 }

Task.Yield()呼び出しがなければ、メソッドはawaitの最初の呼び出しまで同期的に実行されます。

191
Reed Copsey

内部的に、SynchronizationContext.Currentnullである場合、await Task.Yield()は現在の同期コンテキストまたはランダムプールスレッドのいずれかで単に継続をキューに入れます。

効率的に実装 カスタムの待機者として。同じ効果を生成する効率の低いコードは、次のように単純な場合があります。

var tcs = new TaskCompletionSource<bool>();
var sc = SynchronizationContext.Current;
if (sc != null)
    sc.Post(_ => tcs.SetResult(true), null);
else
    ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(true));
await tcs.Task;

Task.Yield()は、奇妙な実行フローの変更のショートカットとして使用できます。例えば:

async Task DoDialogAsync()
{
    var dialog = new Form();

    Func<Task> showAsync = async () => 
    {
        await Task.Yield();
        dialog.ShowDialog();
    }

    var dialogTask = showAsync();
    await Task.Yield();

    // now we're on the dialog's nested message loop started by dialog.ShowDialog 
    MessageBox.Show("The dialog is visible, click OK to close");
    dialog.Close();

    await dialogTask;
    // we're back to the main message loop  
}

とはいえ、Task.Yield()Task.Factory.StartNewで適切なタスクスケジューラーで置き換えることができない場合は考えられません。

こちらもご覧ください:

28
noseratio

Task.Yield()の使用法の1つは、非同期再帰を行うときにスタックオーバーフローを防ぐことです。 Task.Yield()は、同期の継続を防ぎます。

private static void Main()
    {
        RecursiveMethod().Wait();
    }

    private static async Task RecursiveMethod()
    {
        await Task.Delay(1);
        //await Task.Yield(); // Uncomment this line to prevent stackoverlfow.
        await RecursiveMethod();
    }
1
Joakim M. H.