Estoy tratando de entender async/await y pensé que entendía algunas cosas sobre el uso. Pero aún no está muy claro cuál sería el beneficio real en un escenario como el que se muestra a continuación.
Mire el uso de Task.Run. El primer método usa un delegado normal y usa Thread.Sleep, pero el segundo usa el delegado 'asincrónico' y Task.Delay.
Mi pregunta es: ¿cómo hace esto alguna diferencia en este método (o no)?
El método en sí es un método asíncrono. El código está creando un subproceso separado (a través de Task.Run) y este subproceso no tiene nada más que hacer que ejecutar ese delegado. Entonces, incluso si cede con una espera en Task.Delay, ¿cuál es el uso en este escenario, ya que el hilo es de todos modos un hilo aislado que no se usa para nada más e incluso si solo usa Thread.Sleep, el hilo aún estaría contextualizado? cambiar para ceder el paso a otros subprocesos para el procesador.
// The task thread uses a async delegate public async Task<bool> RetrySendEmail(MailMessage message) { bool emailSent = false; await (Task.Run(***async ()*** => { for (int i = 0; i < 3; i++) { if (emailSent) break; else // Wait for 5 secs before trying again ***await Task.Delay(5000);*** try { Smtphost.Send(message); emailSent = true; break; } catch (Exception e) { emailSent = false; // log; } } return emailSent; })); } // The task thread uses a normal delegate public async Task<bool> RetrySendEmail(MailMessage message) { bool emailSent = false; await (Task.Run(***()*** => { for (int i = 0; i < 3; i++) { if (emailSent) break; else // Wait for 5 secs before trying again ***Thread.Sleep(5000);*** try { Smtphost.Send(message); emailSent = true; break; } catch (Exception e){ emailSent = false; // log; } } return emailSent; })); }Mi pregunta es: ¿cómo hace esto alguna diferencia en este método (o no)?
Un par de diferencias
async dentro Task.Run significa que realmente ejecuta una Task<Task> . Esto está oculto para usted por el hecho de que Task.Run es consciente de la sincronización y desenvuelve la tarea interna por usted, algo que Task.Factory.StartNew no hizo Cuando usa un delegado asíncrono con Task.Run , crea un nuevo hilo, luego cede el control una vez que presiona await Task.Delay . La continuación se ejecutará en un subproceso de grupo de subprocesos arbitrario. Además, el compilador transforma el delegado en una máquina de estado.
Con el delegado normal, crea un hilo, lo bloquea sincrónicamente durante 5 segundos y luego continúa en el punto donde lo dejó. Sin máquinas de estado, sin ceder.
Entonces, incluso si cede con una espera en Task.Delay, ¿cuál es el uso en este escenario, ya que el hilo es de todos modos un hilo aislado que no se usa para nada más e incluso si solo usa Thread.Sleep, el hilo aún estaría contextualizado? cambiar para ceder el paso a otros subprocesos para el procesador.
El uso de async con Task.Run puede ser cuando desea realizar trabajo vinculado tanto a la CPU como a la E/S, todo en un subproceso dedicado. Tiene razón al pensar que después de que el delegado asíncrono cede, regresa en un subproceso arbitrario. Sin embargo, si no usó Task.Run y el método async se ejecutó desde un subproceso que tenía un contexto de sincronización personalizado adjunto (como WinformsSynchronizationContext), cualquier trabajo posterior a la await volvería al bucle de mensajes de la interfaz de usuario, a menos que usara ConfigureAwait(false) .
A decir verdad, no he visto muchos escenarios en los que Task.Run y async se usen correctamente. Pero a veces tiene sentido.
La diferencia es que está desperdiciando un hilo y su intervalo de tiempo asignado.
Cuando bloquea un subproceso durante 5 segundos, ese subproceso no se puede usar en otras partes de su sistema para realizar el trabajo real de la CPU. También crea un cambio de contexto ya que ese hilo no puede hacer nada más.
Cuando libera ese subproceso mediante Task.Delay en lugar de Thread.Sleep , el subproceso puede volver a ThreadPool , tomar una tarea en espera y ejecutarla.
Sobre todo, cuando libera sus subprocesos cuando puede, hace que su aplicación sea más escalable y eficiente, ya que necesita menos recursos para hacer el mismo trabajo o la misma cantidad de recursos para hacer más trabajo.
Cuando su operación es realmente asíncrona, no es necesario usar Task.Run (a menos que necesite un subproceso en segundo plano). Simplemente puede llamar a ese método y esperar la tarea devuelta:
public async Task<bool> RetrySendEmail(MailMessage message) { bool emailSent = false; for (int i = 0; i < 3; i++) { if (emailSent) break; else await Task.Delay(5000); try { Smtphost.Send(message); emailSent = true; break; } catch (Exception e) { emailSent = false; // log; } } return emailSent; }