Я использую async / await и Task
много, но никогда не использовал Task.Yield()
и, честно говоря, даже со всеми объяснениями, я не понимаю, зачем мне этот метод.
Может кто-нибудь привести хороший пример, где Yield()
это требуется?
источник
Я использую async / await и 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()
метод заставляет метод быть асинхронным, зачем нам писать «настоящий» асинхронный код? Представьте себе тяжелый метод синхронизации. Чтобы сделать это асинхронным, просто добавьтеasync
иawait Task.Yield()
в начале, и волшебным образом, это будет асинхронно? Это было бы похоже на обертывание всего кода синхронизацииTask.Run()
и создание ложного асинхронного метода.Task.Run
для его реализации,ExecuteFooOnUIThread
будет работать в пуле потоков, а не поток пользовательского интерфейса. С помощьюawait Task.Yield()
этого вы заставляете его быть асинхронным таким образом, чтобы последующий код все еще выполнялся в текущем контексте (только в более поздний момент времени). Это не то, что вы обычно делаете, но приятно, что есть опция, если она требуется по какой-то странной причине.ExecuteFooOnUIThread()
работал очень долго, он в какой-то момент блокировал бы поток пользовательского интерфейса на долгое время и сделал бы пользовательский интерфейс невосприимчивым, это правильно?Внутренне
await Task.Yield()
просто помещает в очередь продолжение либо в текущем контексте синхронизации, либо в потоке случайного пула, если онSynchronizationContext.Current
естьnull
.Он эффективно реализован как пользовательский ожидающий. Менее эффективный код, производящий идентичный эффект, может быть таким простым:
Task.Yield()
может использоваться как ярлык для некоторых странных изменений потока выполнения. Например:Тем не менее, я не могу вспомнить ни одного случая, когда
Task.Yield()
не может быть заменен сTask.Factory.StartNew
/ надлежащим планировщиком задач.Смотрите также:
"await Task.Yield ()" и его альтернативы
Task.Yield - реальные использования?
источник
var dialogTask = await showAsync();
?var dialogTask = await showAsync()
не будет компилироваться, потому чтоawait showAsync()
выражение не возвращает aTask
(в отличие от этого безawait
). Тем не менее, если вы это сделаетеawait showAsync()
, выполнение после того, как оно будет возобновлено только после закрытия диалога, вот как это отличается. Это потому, чтоwindow.ShowDialog
синхронный API (несмотря на то, что он по-прежнему качает сообщения). В этом коде я хотел продолжить, пока диалог все еще отображается.Одним из применений
Task.Yield()
является предотвращение переполнения стека при выполнении асинхронной рекурсии.Task.Yield()
предотвращает синхронное продолжение. Обратите внимание, однако, что это может вызвать исключение OutOfMemory (как отметил Triynko). Бесконечная рекурсия все еще небезопасна, и вам, вероятно, лучше переписать рекурсию как цикл.источник
await Task.Delay(1)
достаточно, чтобы предотвратить это. (Консольное приложение, .NET Core 3.1, C # 8)Task.Yield()
может использоваться в имитационных реализациях асинхронных методов.источник