Когда я буду использовать Task.Yield()?

Я использую async / await и Task много, но никогда не использую Task.Yield() и если честно, даже со всеми объяснениями, я не понимаю, зачем мне нужен этот метод.

может кто-нибудь дать хороший пример, когда - это?

3 ответов


при использовании 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() просто ставит продолжение в очередь либо в текущем контексте синхронизации, либо в потоке случайного пула, если SynchronizationContext.Current is null.

это эффективно как обычай awaiter. Менее эффективный код, производящий идентичный эффект, может быть таким простым:

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 w / правильный планировщик задач.

Читайте также:


Task.Yield() может использоваться в макетных реализациях асинхронных методов.