Actualmente estoy leyendo " Concurrencia en C # Cookbook " de 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 se timeoutTask
está ejecutando Task.Delay
.
En el caso de que no se haya agotado el tiempo de espera, entonces downloadTask
ya está completado. ¿Por qué es necesario esperar un segundo en lugar de regresar downloadTask.Result
, dado que la tarea ya está completada?
c#
asynchronous
async-await
task
julio.g
fuente
fuente
downloadTask
ytimeoutTask
? ¿Qué hacen?AggregateException
conResult
vs primera excepción víaExceptionDispatchInfo
conawait
). Discutido con más detalle en "Task Exception Handling in .NET 4.5" de Stephen Toub: blogs.msdn.com/b/pfxteam/archive/2011/09/28/… )Respuestas:
Ya hay algunas buenas respuestas / comentarios aquí, pero solo para intervenir ...
Hay dos razones por las que prefiero
await
sobreResult
(oWait
). La primera es que el manejo de errores es diferente;await
no envuelve la excepción en unAggregateException
. Idealmente, el código asincrónico nunca debería tener que lidiarAggregateException
en absoluto, a menos que lo desee específicamente .La segunda razón es un poco más sutil. Como describo en mi blog (y en el libro),
Result
/Wait
puede causar interbloqueos y puede causar interbloqueos aún más sutiles cuando se usa en unasync
método . Entonces, cuando estoy leyendo el código y veo unResult
oWait
, es una señal de advertencia inmediata. LaResult
/Wait
solo es correcta si está absolutamente seguro de que la tarea ya se completó. Esto no solo es difícil de ver de un vistazo (en el código del mundo real), sino que también es más frágil para los cambios de código.Eso no quiere decir que
Result
/Wait
debe no ser utilizado. Sigo estas pautas en mi propio código:await
.Result
/Wait
si el código realmente lo requiere. Tal uso probablemente debería tener comentarios.Result
yWait
.Tenga en cuenta que (1) es, con mucho, el caso común, de ahí mi tendencia a usar en
await
todas partes y tratar los otros casos como excepciones a la regla general.fuente
await
evita laAggregateException
envoltura.AggregateException
fue diseñado para programación paralela, no programación asincrónica.Wait
era unirse a instancias de Dynamic Task ParallelismTask
. Usarlo para esperarTask
instancias asincrónicas es peligroso. Microsoft consideró introducir un nuevo tipo de "Promesa", pero optó por utilizar el existente en suTask
lugar; la compensación de reutilizar elTask
tipo existente para tareas asincrónicas es que terminas con varias API que simplemente no deberían usarse en código asincrónico.Esto tiene sentido si
timeoutTask
es un producto deTask.Delay
lo que creo que está en el libro.Task.WhenAny
devuelveTask<Task>
, donde la tarea interna es una de las que pasó como argumentos. Se podría reescribir así:En cualquier caso, debido a que
downloadTask
ya se completó, hay una diferencia mínima entrereturn await downloadTask
yreturn downloadTask.Result
. Es en que este último arrojará loAggregateException
que envuelve cualquier excepción original, como lo señaló @KirillShlenskiy en los comentarios. El primero simplemente volvería a lanzar la excepción original.En cualquier caso, donde sea que maneje las excepciones, debe verificar
AggregateException
sus excepciones internas de todos modos, para llegar a la causa del error.fuente