170

私はasync / awaitを使っていますTaskたくさん使っていますが、使ったことがないTask.Yield()そして、私がこの方法がなぜ必要なのか理解していないすべての説明でさえ正直に言うと。

誰かが良い例をあげることができますYield()必要とされている?

3 답변


173

使うとき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


  • ここで何かを誤って解釈しているようです。もしawait Task.Yield()メソッドを非同期にするように強制します。なぜ、" real"と書くのが面倒になるのでしょうか。非同期コード?重い同期方法を想像してください。非同期にするには、単に追加するだけです。asyncそしてawait Task.Yield()初めに魔法のように、それは非同期になりますか?これは、ほとんどすべての同期コードを次のコードにラップするようなものです。Task.Run()そして偽の非同期メソッドを作成します。 - Krumelur
  • @ Krumelur大きな違いがあります - 私の例を見てください。あなたが使用する場合Task.Run実装するExecuteFooOnUIThreadUIスレッドではなく、スレッドプールで実行されます。ありawait Task.Yield()つまり、後続のコードが現在のコンテキストで実行されるように(後の時点で)非同期にするよう強制します。それはあなたが通常することではありませんが、それが何らかの奇妙な理由で必要とされるならオプションがあるのはうれしいです。 - Reed Copsey
  • もう1つ質問があります。ExecuteFooOnUIThread()非常に長い間実行されていました、それはまだある時点で長い間UIスレッドをブロックして、UIを無反応にするでしょう、それは正しいですか? - Krumelur
  • @ Krumelurはい、そうです。すぐにではなく - それは後で起こります。 - Reed Copsey
  • この答えは技術的には正しいですが、「残りのコードは後で実行されます」という記述は、抽象的すぎて誤解を招く可能性があります。 Task.Yield()以降のコードの実行スケジュールは、具象SynchronizationContextに大きく依存します。また、MSDNのドキュメントでは、"ほとんどのUI環境でUIスレッドに存在する同期コンテキストは、入力およびレンダリング作業よりも高いコンテキストに投稿された作業を優先することが多いと明言されています。このため、Task.Yield()を待つことに頼らないでください。 UIの反応をよくするために。」 - Vitaliy Tsvayer

26

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

それは効率的に実施されたカスタムウェイターとして。同じ効果を生み出す効率の悪いコードは、これと同じくらい単純かもしれません。

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.StartNeww /適切なタスクスケジューラ

また見なさい:


-3

Task.Yield()非同期メソッドのモック実装で使用されるかもしれません。


リンクされた質問


関連する質問

最近の質問