Estou usando async / waitit e Task
muito, mas nunca usei Task.Yield()
e para ser honesto, mesmo com todas as explicações, não entendo por que precisaria desse método.
Alguém pode dar um bom exemplo onde Yield()
é necessário?
fonte
Estou usando async / waitit e Task
muito, mas nunca usei Task.Yield()
e para ser honesto, mesmo com todas as explicações, não entendo por que precisaria desse método.
Alguém pode dar um bom exemplo onde Yield()
é necessário?
Quando você usa async
/ await
, não há garantia de que o método que você chama ao await FooAsync()
executar seja executado de forma assíncrona. A implementação interna é livre para retornar usando um caminho completamente síncrono.
Se você estiver criando uma API em que é fundamental que você não bloqueie e execute algum código de forma assíncrona, e há uma chance de o método chamado ser executado de forma síncrona (efetivamente bloqueada), usar await Task.Yield()
forçará seu método a ser assíncrono e retornará controle nesse ponto. O restante do código será executado posteriormente (nesse ponto, ele ainda poderá ser executado de forma síncrona) no contexto atual.
Isso também pode ser útil se você criar um método assíncrono que requer alguma inicialização "de longa execução", ou seja:
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);
}
Sem a Task.Yield()
chamada, o método será executado de forma síncrona até a primeira chamada para await
.
await Task.Yield()
força o método a ser assíncrono, por que nos preocuparíamos em escrever código assíncrono "real"? Imagine um método de sincronização pesada. Para torná-lo assíncrono, basta adicionarasync
eawait Task.Yield()
no início e magicamente, será assíncrono? Isso seria como envolver todo o código de sincronizaçãoTask.Run()
e criar um método assíncrono falso.Task.Run
para implementá-lo,ExecuteFooOnUIThread
será executado no pool de threads, não no thread da interface do usuário. Comawait Task.Yield()
, você o força a ser assíncrono, de forma que o código subsequente ainda seja executado no contexto atual (apenas posteriormente). Não é algo que você normalmente faria, mas é bom que exista a opção se for necessária por algum motivo estranho.ExecuteFooOnUIThread()
execução fosse muito longa, ele ainda bloqueará o thread da interface do usuário por um longo período de tempo e deixaria a interface sem resposta, está correto?Internamente,
await Task.Yield()
basta enfileirar a continuação no contexto de sincronização atual ou em um encadeamento de pool aleatório, seSynchronizationContext.Current
fornull
.É implementado eficientemente como garçom personalizado. Um código menos eficiente que produz o efeito idêntico pode ser tão simples quanto isto:
Task.Yield()
pode ser usado como atalho para algumas alterações estranhas no fluxo de execução. Por exemplo:Dito isto, não consigo pensar em nenhum caso em
Task.Yield()
que não possa ser substituído porTask.Factory.StartNew
um agendador de tarefas adequado.Veja também:
"aguarde Task.Yield ()" e suas alternativas
Task.Yield - usos reais?
fonte
var dialogTask = await showAsync();
?var dialogTask = await showAsync()
não será compilado porque aawait showAsync()
expressão não retorna aTask
(ao contrário de semawait
). Dito isto, se você fizerawait showAsync()
, a execução depois que ela será retomada somente após o fechamento do diálogo, é assim que é diferente. Issowindow.ShowDialog
ocorre porque é uma API síncrona (apesar de ainda gerar mensagens). Nesse código, eu queria continuar enquanto a caixa de diálogo ainda é mostrada.Um dos usos
Task.Yield()
é evitar um estouro de pilha ao fazer recursão assíncrona.Task.Yield()
impede a continuação síncrona. Observe, no entanto, que isso pode causar uma exceção OutOfMemory (conforme observado por Triynko). A recursão infinita ainda não é segura e é melhor reescrever a recursão como um loop.fonte
await Task.Delay(1)
suficiente para impedi-lo. (Aplicativo para console, .NET Core 3.1, C # 8)Task.Yield()
pode ser usado em implementações simuladas de métodos assíncronos.fonte