Я использую async/wait и Task много, но никогда не использовал Task.Yield() и, честно говоря, даже со всеми объяснениями, которые я не понимаю, зачем мне нужен этот метод.
Может ли кто-нибудь дать хороший пример, где Yield() требуется?
Я использую async/wait и Task много, но никогда не использовал Task.Yield() и, честно говоря, даже со всеми объяснениями, которые я не понимаю, зачем мне нужен этот метод.
Может ли кто-нибудь дать хороший пример, где Yield() требуется?
Когда вы используете 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 равен 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 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 с правильным планировщиком задач.
Смотрите также:
Одним из применений Task.Yield() является предотвращение при выполнении асинхронной рекурсии. 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();
    }
		У меня есть базовый класс, на котором есть первичный метод:
public async Task SendAsync (message); 
Затем я решил позже указать, что мне нужно было проверить сообщение, отправленное в более продвинутом варианте базового класса, к которому я добавил два базовых метода:
protected virtual async Task OnBeforeSend(message);
protected virtual async Task OnAfterSend(message);
В специализированной версии моего класса я добавил реализации методов до и после отправки сообщения, но не нужно было добавлять пользовательские методы до и после отправки в мою базовую реализацию класса. Мне потребовалось, чтобы моя базовая реализация также была конкретного типа, поскольку она использовалась где-то еще.
Чтобы сохранить компилятор счастливым, мне нужно было предоставить результат задачи для базовых (пустых) реализаций, и для этого я использовал подход Task.Yield(). Поэтому в моей реализации базового класса у меня есть следующее:
protected virtual async Task OnBeforeSend(IEmailMessage mailMessage)
{
    await Task.Yield();
}
Кажется, это хорошо работает для меня, и я не вижу причины, почему это было бы неправильно.
Task.Yield() может использоваться в имитационных реализациях асинхронных методов.