Alguém pode explicar se await
e ContinueWith
são sinônimos ou não no exemplo a seguir. Estou tentando usar o TPL pela primeira vez e tenho lido toda a documentação, mas não entendo a diferença.
Aguarde :
String webText = await getWebPage(uri);
await parseData(webText);
ContinueWith :
Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) => parseData(task.Result));
webText.Start();
continue.Wait();
É um preferido sobre o outro em situações particulares?
c#
task-parallel-library
task
async-await
Harrison
fonte
fonte
Wait
chamada no segundo exemplo , os dois fragmentos seriam (em sua maioria) equivalentes.getWebPage
método não pode ser usado em ambos os códigos. No primeiro código, ele possui umTask<string>
tipo de retorno, enquanto no segundo ele possui umstring
tipo de retorno. então, basicamente, seu código não compila. - se para ser preciso.Respostas:
No segundo código, você está aguardando de forma síncrona a conclusão da continuação. Na primeira versão, o método retornará ao chamador assim que atingir a primeira
await
expressão que ainda não foi concluída.Eles são muito semelhantes no sentido de que ambos agendam uma continuação, mas assim que o fluxo de controle se torna ainda mais complexo,
await
leva a um código muito mais simples. Além disso, conforme observado por Servy nos comentários, aguardar uma tarefa "desembrulhará" exceções agregadas, o que geralmente leva a um tratamento de erros mais simples. O uso tambémawait
agendará implicitamente a continuação no contexto de chamada (a menos que você useConfigureAwait
). Não é nada que não possa ser feito "manualmente", mas é muito mais fácil fazê-lo comawait
.Eu sugiro que você tente implementar uma sequência ligeiramente maior de operações com ambos
await
eTask.ContinueWith
- pode ser uma verdadeira revelação.fonte
await
overContinueWith
nesse aspecto.parseData
executado.Aqui está a sequência de trechos de código que usei recentemente para ilustrar a diferença e vários problemas usando soluções assíncronas.
Suponha que você tenha algum manipulador de eventos em seu aplicativo baseado em GUI que leve muito tempo e, portanto, você gostaria de torná-lo assíncrono. Esta é a lógica síncrona com a qual você começa:
LoadNextItem retorna uma Task, que eventualmente produzirá algum resultado que você gostaria de inspecionar. Se o resultado atual for o que você está procurando, atualize o valor de algum contador na IU e retorne do método. Caso contrário, você continuará processando mais itens de LoadNextItem.
Primeira ideia para a versão assíncrona: use apenas continuações! E vamos ignorar a parte do loop por enquanto. Quer dizer, o que poderia dar errado?
Ótimo, agora temos um método que não bloqueia! Em vez disso, ele trava. Quaisquer atualizações nos controles da IU devem acontecer no encadeamento da IU, portanto, você precisará levar em conta isso. Felizmente, há uma opção para especificar como as continuações devem ser agendadas, e há um padrão apenas para isso:
Ótimo, agora temos um método que não trava! Em vez disso, ele falha silenciosamente. As continuações são tarefas separadas, com seu status não vinculado ao da tarefa anterior. Portanto, mesmo se LoadNextItem falhar, o chamador verá apenas uma tarefa que foi concluída com êxito. Ok, então apenas passe a exceção, se houver uma:
Ótimo, agora isso realmente funciona. Para um único item. Agora, que tal aquele loop. Acontece que uma solução equivalente à lógica da versão síncrona original será mais ou menos assim:
Ou, em vez de todos os itens acima, você pode usar o assíncrono para fazer a mesma coisa:
Isso é muito melhor agora, não é?
fonte