whenall - task httpresponsemessage c#
Esperar en una tarea completada como la tarea. ¿Resultado? (2)
Actualmente estoy leyendo "Concurrency in C # Cookbook" por Stephen Cleary, y noté la siguiente técnica:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask es una llamada a httpclient.GetStringAsync, y timeoutTask está ejecutando Task.Delay.
En caso de que no haya expirado, entonces downloadTask ya está completo. ¿Por qué es necesario esperar un segundo en lugar de devolver downloadTask.Result, dado que la tarea ya está completa?
Esto tiene sentido si timeoutTask
es un producto de Task.Delay
, que creo que es lo que está en el libro.
Task.WhenAny
devuelve Task<Task>
, donde la tarea interna es una de las que pasó como argumentos. Podría ser reescrito así:
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
En cualquier caso, debido a que downloadTask
ya se ha completado, hay una diferencia muy pequeña entre return await downloadTask
y return downloadTask.Result
. Es en este último que arrojará AggregateException
que envuelve cualquier excepción original, como lo señala @KirillShlenskiy en los comentarios. El primero solo volvería a tirar la excepción original.
En cualquier caso, siempre que maneje excepciones, debe buscar AggregateException
y sus excepciones internas de todos modos, para llegar a la causa del error.
Ya hay algunas buenas respuestas / comentarios aquí, pero solo para sonar en ...
Hay dos razones por las que prefiero await
sobre el Result
(o Wait
). El primero es que el manejo de errores es diferente; await
no envuelve la excepción en una AggregateException
. Idealmente, el código asíncrono nunca debería tener que tratar con AggregateException
en absoluto, a menos que específicamente lo desee .
La segunda razón es un poco más sutil. Como lo describo en mi blog (y en el libro), Result
/ Wait
puede causar interbloqueos y puede causar interbloqueos aún más sutiles cuando se utiliza en un método async
. Entonces, cuando estoy leyendo el código y veo un Result
o Wait
, eso es un indicador de advertencia inmediato. El Result
/ Wait
solo es correcto si está absolutamente seguro de que la tarea ya está completa. No solo es difícil de ver de un vistazo (en el código del mundo real), sino que también es más quebradizo para los cambios de código.
Eso no quiere decir que el Result
/ Wait
nunca debería ser usado. Sigo estas pautas en mi propio código:
- El código asíncrono en una aplicación solo puede usar
await
. - El código de utilidad asíncrono (en una biblioteca) ocasionalmente puede usar
Result
/Wait
si el código realmente lo requiere. Tal uso probablemente debería tener comentarios. - El código de tarea paralelo puede usar
Result
yWait
.
Tenga en cuenta que (1) es, con mucho, el caso común, por lo tanto, mi tendencia a usar await
todas partes y tratar los otros casos como excepciones a la regla general.