c# - que - ¿Qué diferencia hace? ¿Ejecutar un delegado de acción ''asincrónico'' con un Task.Run(frente a un delegado de acción predeterminado)?
eventos en c# ejemplos (2)
La diferencia es que estás desperdiciando un hilo y su porción de tiempo asignada.
Cuando bloquea un hilo durante 5 segundos, ese hilo no se puede usar en otras partes de su sistema para hacer el trabajo real de la CPU. También crea un cambio de contexto ya que ese hilo no puede hacer otra cosa.
Cuando Task.Delay
ese hilo usando Task.Delay
lugar de Thread.Sleep
el hilo puede volver al ThreadPool
, tomar una tarea en espera y ejecutarla.
Sobre todo, cuando liberas tus hilos cuando puedes, haces que tu 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 utilizar Task.Run
(a menos que necesite un hilo de fondo). 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;
}
Estoy tratando de asentir / esperar y pensé que entendía algunas cosas sobre el uso. Pero aún no está del todo claro cuál sería el beneficio real en un escenario como el de abajo.
Mire el uso de Task.Run. El primer método usa un delegado normal y usa Thread.Sleep pero el segundo usa delegado ''async'' 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 hilo separado (a través de Task.Run) y este hilo no tiene nada más que hacer que ejecutar ese delegado. Por lo tanto, 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 no utilizado para nada más e incluso si solo utiliza Thread.Sleep, el hilo aún contextualizaría cambiar para ceder a otros hilos 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
- Usar un delegado
async
dentro deTask.Run
significa que realmente ejecuta unaTask<Task>
. Esto está oculto para ti por el hecho de queTask.Run
es asíncrono y desenvuelve la tarea interna para ti, algo queTask.Factory.StartNew
no hizo Cuando utilizas un delegado asíncrono con
Task.Run
, creas un nuevo hilo y luegoawait Task.Delay
control una vez queawait Task.Delay
. La continuación se ejecutará en un subproceso de grupo de subprocesos arbitrario. Además, el delegado se transforma en una máquina de estados por el compilador.Con el delegado normal, creas un hilo, lo bloqueas sincrónicamente durante 5 segundos y luego continúas en el punto donde lo dejaste. Sin máquinas de estado, sin rendimiento.
Por lo tanto, 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 no utilizado para nada más e incluso si solo utiliza Thread.Sleep, el hilo aún contextualizaría cambiar para ceder a otros hilos para el procesador.
El uso de Task.Run
con Task.Run
puede ser cuando desee realizar trabajos en CPU y IO encuadernados, todo en un hilo dedicado. Tiene razón al pensar que después de que el delegado asíncrono rinda, regresa en un hilo arbitrario. Sin embargo, si no hubiera utilizado Task.Run
y el método async
ejecutado desde un subproceso que tuviera un contexto de sincronización personalizado adjunto (como WinformsSynchronizationContext), cualquier trabajo posterior al await
devolvería al bucle de mensaje de UI, a menos que lo haya utilizado ConfigureAwait(false)
.
A decir verdad, no he visto tantos escenarios donde Task.Run
y async
se usan correctamente. Pero tiene sentido a veces.