¿Alguien puede explicar si await
y ContinueWith
son sinónimos o no en el siguiente ejemplo? Estoy tratando de usar TPL por primera vez y he estado leyendo toda la documentación, pero no entiendo la diferencia.
Espera :
String webText = await getWebPage(uri);
await parseData(webText);
Continuar con :
Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) => parseData(task.Result));
webText.Start();
continue.Wait();
¿Se prefiere uno sobre el otro en situaciones particulares?
c#
task-parallel-library
task
async-await
Harrison
fuente
fuente
Wait
llamada en el segundo ejemplo , los dos fragmentos serían (en su mayoría) equivalentes.getWebPage
método no se puede utilizar en ambos códigos. En el primer código tiene unTask<string>
tipo de retorno mientras que en el segundo tiene unstring
tipo de retorno. así que básicamente su código no se compila. - para ser precisos.Respuestas:
En el segundo código, está esperando sincrónicamente a que se complete la continuación. En la primera versión, el método volverá a la persona que llama tan pronto como llegue a la primera
await
expresión que aún no está completa.Son muy similares en el sentido de que ambos programan una continuación, pero tan pronto como el flujo de control se vuelve un poco complejo,
await
conduce a un código mucho más simple. Además, como señaló Servy en los comentarios, esperar una tarea "desenvolverá" las excepciones agregadas, lo que generalmente conduce a un manejo de errores más simple. Además, el usoawait
programará implícitamente la continuación en el contexto de llamada (a menos que useConfigureAwait
). No es nada que no se pueda hacer "manualmente", pero es mucho más fácil hacerloawait
.Le sugiero que intente implementar una secuencia de operaciones un poco más grande con ambos
await
yTask.ContinueWith
, puede ser una verdadera revelación.fuente
await
overContinueWith
en ese sentido.parseData
ejecuta.Aquí está la secuencia de fragmentos de código que utilicé recientemente para ilustrar la diferencia y varios problemas usando soluciones asíncronas.
Suponga que tiene algún controlador de eventos en su aplicación basada en GUI que requiere mucho tiempo y, por lo tanto, le gustaría hacerlo asincrónico. Aquí está la lógica síncrona con la que comienza:
LoadNextItem devuelve una tarea, que eventualmente producirá algún resultado que le gustaría inspeccionar. Si el resultado actual es el que está buscando, actualice el valor de algún contador en la interfaz de usuario y regrese del método. De lo contrario, continuará procesando más elementos de LoadNextItem.
Primera idea para la versión asincrónica: ¡solo usa continuaciones! E ignoremos la parte del bucle por el momento. Quiero decir, ¿qué podría salir mal?
¡Genial, ahora tenemos un método que no bloquea! En cambio, se estrella. Cualquier actualización de los controles de la interfaz de usuario debería ocurrir en el hilo de la interfaz de usuario, por lo que deberá tenerlo en cuenta. Afortunadamente, hay una opción para especificar cómo se deben programar las continuaciones, y hay una predeterminada solo para esto:
¡Genial, ahora tenemos un método que no falla! En cambio, falla silenciosamente. Las continuaciones son tareas separadas en sí mismas, y su estado no está ligado al de la tarea anterior. Entonces, incluso si LoadNextItem falla, la persona que llama solo verá una tarea que se haya completado con éxito. Bien, entonces simplemente pase la excepción, si hay una:
Genial, ahora esto realmente funciona. Por un solo artículo. Ahora, ¿qué tal ese bucle? Resulta que una solución equivalente a la lógica de la versión síncrona original se verá así:
O, en lugar de todo lo anterior, puede usar async para hacer lo mismo:
Eso es mucho mejor ahora, ¿no?
fuente