Esperar una tarea completada igual que la tarea. ¿Resultado?

Resuelto julio.g asked hace 10 años • 2 respuestas

Actualmente estoy leyendo " Libro de recetas de simultaneidad en C# " de Stephen Cleary y noté la siguiente técnica:

var completedTask = await Task.WhenAny(downloadTask, timeoutTask);  
if (completedTask == timeoutTask)  
  return null;  
return await downloadTask;  

downloadTaskes una llamada a httpclient.GetStringAsyncy timeoutTaskse está ejecutando Task.Delay.

En caso de que no haya expirado el tiempo de espera, ya downloadTaskse habrá completado. ¿Por qué es necesario hacer una segunda espera en lugar de regresar downloadTask.Result, dado que la tarea ya está completada?

julio.g avatar Jul 08 '14 10:07 julio.g
Aceptado

Ya hay algunas buenas respuestas/comentarios aquí, pero solo para intervenir...

Hay dos razones por las que prefiero await( Resulto Wait). La primera es que el manejo de errores es diferente; awaitno envuelve la excepción en un archivo AggregateException. Idealmente, el código asincrónico nunca debería tener que lidiar con AggregateExceptionnada, a menos que así lo desee específicamente .

La segunda razón es un poco más sutil. Como lo describo en mi blog (y en el libro), Result/ Waitpuede causar interbloqueos y puede causar interbloqueos aún más sutiles cuando se usa en un asyncmétodo . Entonces, cuando leo el código y veo un Resulto Wait, es una señal de advertencia inmediata. El Result/ Waitsolo es correcto si está absolutamente seguro de que la tarea ya se completó. Esto no sólo 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/ nuncaWait deba usarse. Sigo estas pautas en mi propio código:

  1. El código asincrónico en una aplicación solo puede usar await.
  2. El código de utilidad asincrónico (en una biblioteca) ocasionalmente puede usar Result/ Waitsi el código realmente lo requiere. Este uso probablemente debería tener comentarios.
  3. El código de tarea paralelo puede usar Resulty Wait.

Tenga en cuenta que (1) es, con diferencia, el caso común, de ahí mi tendencia a utilizarlo awaiten todas partes y tratar los demás casos como excepciones a la regla general.

Stephen Cleary avatar Jul 09 '2014 14:07 Stephen Cleary

Esto tiene sentido si timeoutTaskes un producto de Task.Delay, lo cual creo que está en el libro.

Task.WhenAnydevuelve Task<Task>, donde la tarea interna es una de las que pasaste como argumentos. Podría reescribirse así:

Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)  
  return null;  
return downloadTask.Result; 

En cualquier caso, como downloadTaskya se completó, hay una diferencia mínima entre return await downloadTasky return downloadTask.Result. Es que este último arrojará AggregateExceptionlo 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, dondequiera que maneje excepciones, debe verificar AggregateExceptionsus excepciones internas de todos modos para llegar a la causa del error.

noseratio avatar Jul 08 '2014 04:07 noseratio