Estou escrevendo um aplicativo WinForms que transfere dados para um dispositivo de classe USB HID. Meu aplicativo usa a excelente biblioteca HID genérica v6.0, que pode ser encontrada aqui . Em poucas palavras, quando preciso gravar dados no dispositivo, este é o código que é chamado:
private async void RequestToSendOutputReport(List<byte[]> byteArrays)
{
foreach (byte[] b in byteArrays)
{
while (condition)
{
// we'll typically execute this code many times until the condition is no longer met
Task t = SendOutputReportViaInterruptTransfer();
await t;
}
// read some data from device; we need to wait for this to return
RequestToGetInputReport();
}
}
Quando meu código sai do loop while, preciso ler alguns dados do dispositivo. No entanto, o dispositivo não pode responder imediatamente, por isso preciso aguardar o retorno dessa ligação antes de continuar. Como existe atualmente, RequestToGetInputReport () é declarado assim:
private async void RequestToGetInputReport()
{
// lots of code prior to this
int bytesRead = await GetInputReportViaInterruptTransfer();
}
Quanto vale a pena, a declaração para GetInputReportViaInterruptTransfer () é semelhante a esta:
internal async Task<int> GetInputReportViaInterruptTransfer()
Infelizmente, não estou muito familiarizado com o funcionamento das novas tecnologias async / wait no .NET 4.5. Eu fiz uma pequena leitura anterior sobre a palavra-chave wait e isso me deu a impressão de que a chamada para GetInputReportViaInterruptTransfer () dentro de RequestToGetInputReport () esperaria (e talvez espere?), Mas não parece ser a chamada para RequestToGetInputReport () em si está esperando porque parece que eu estou entrando novamente no loop while quase imediatamente?
Alguém pode esclarecer o comportamento que estou vendo?
fonte
void
paraTask
exatamente como você havia dito.GetAwaiter().GetResult()
Task
representa a execução do método - portanto, osreturn
valores são colocadosTask.Result
e as exceções são colocadasTask.Exception
. Comvoid
, o compilador não tem onde colocar exceções, portanto elas são apenas aumentadas em um thread do conjunto de threads.A coisa mais importante a saber
async
eawait
é queawait
não espera a conclusão da chamada associada. O queawait
faz é retornar o resultado da operação imediatamente e de forma síncrona se a operação já tiver sido concluída ou, se não tiver, agendar uma continuação para executar o restante doasync
método e, em seguida, retornar o controle ao chamador. Quando a operação assíncrona for concluída, a conclusão agendada será executada.A resposta para a pergunta específica no título da sua pergunta é bloquear
async
o valor de retorno de um método (que deve ser do tipoTask
ouTask<T>
) chamando umWait
método apropriado :Nesse trecho de código,
CallGetFooAsyncAndWaitOnResult
é um invólucro síncrono em torno do método assíncronoGetFooAsync
. No entanto, esse padrão deve ser evitado na maior parte do tempo, pois ele bloqueará um encadeamento de conjunto de encadeamentos inteiro durante a operação assíncrona. Esse é um uso ineficiente dos vários mecanismos assíncronos expostos pelas APIs que envidam grandes esforços para fornecê-los.A resposta em "aguardar" não espera a conclusão da chamada tem várias explicações mais detalhadas dessas palavras-chave.
Enquanto isso, a orientação de Stephen Cleary sobre
async void
retenções. Outras explicações interessantes sobre o porquê podem ser encontradas em http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/ e https://jaylee.org/archive/ 2012/07/08 / c-sharp-async-dicas-e-truques-parte-2-async-void.htmlfonte
await
como uma "espera assíncrona" - ou seja, bloqueia o método (se necessário), mas não o encadeamento . Portanto, faz sentido falar emRequestToSendOutputReport
"esperar",RequestToGetInputReport
mesmo que não seja uma espera bloqueadora .A melhor solução para esperar o AsynMethod até concluir a tarefa é
fonte
Aqui está uma solução alternativa usando um sinalizador:
fonte
basta colocar Wait () para aguardar até que a tarefa seja concluída
GetInputReportViaInterruptTransfer().Wait();
fonte
Na verdade, achei isso mais útil para funções que retornam IAsyncAction.
fonte
O fragmento a seguir mostra uma maneira de garantir que o método esperado seja concluído antes de retornar ao chamador. No entanto, eu não diria que é uma boa prática. Por favor, edite minha resposta com explicações, se você pensa o contrário.
fonte
await
+Console.WriteLine
é se tornando umTask
, o que abre mão do controle entre os dois. portanto, sua 'solução' acabará gerando umTask<T>
, que não soluciona o problema. Realizar umTask.Wait
testamento realmente interromperá o processamento (com possibilidades de conflito etc.). Em outras palavras,await
na verdade não esperar, ele simplesmente combina duas porções de forma assíncrona executáveis em um únicoTask
(que alguém pode assistir ou esperar)