puede - task c# ejemplo
¿Await no reanuda el contexto después de la operación asíncrona? (1)
He leído esta pregunta de Noseratio, que muestra un comportamiento en el que TaskScheduler.Current no es el mismo después de que una esperada ha terminado su operación.
La respuesta dice que:
Si no se está ejecutando una tarea real , entonces
TaskScheduler.Currentes lo mismo queTaskScheduler.Default
Cual es verdad . Ya lo vi here :
TaskScheduler.Default
- Devuelve una instancia de
ThreadPoolTaskSchedulerTaskScheduler.Current
- Si se llama desde dentro de una tarea en ejecución, se devolverá el
TaskSchedulerdeTaskSchedulerde la tarea en ejecución actualmente- Si se llama desde cualquier otro lugar, se devolverá
TaskScheduler.Default
Pero luego pensé: si es así, vamos a crear una Task real (y no solo a Task.Yield() ) y probarlo:
async void button1_Click_1(object sender, EventArgs e)
{
var ts = TaskScheduler.FromCurrentSynchronizationContext();
await Task.Factory.StartNew(async () =>
{
MessageBox.Show((TaskScheduler.Current == ts).ToString()); //True
await new WebClient().DownloadStringTaskAsync("http://www.google.com");
MessageBox.Show((TaskScheduler.Current == ts).ToString());//False
}, CancellationToken.None, TaskCreationOptions.None,ts).Unwrap();
}
El primer cuadro de mensaje es "Verdadero", el segundo es "Falso"
Pregunta:
Como puedes ver, creé una tarea real.
Puedo entender por qué el primer MessageBox produce True . Eso es debido a:
Si se llama desde dentro de una tarea en ejecución, se devolverá el Programador de tareas de la tarea en ejecución actualmente
Y esa tarea tiene ts que es el TaskScheduler.FromCurrentSynchronizationContext() enviado TaskScheduler.FromCurrentSynchronizationContext()
Pero, ¿por qué el contexto no se conserva en el segundo MessageBox? Para mí, no estaba claro por la respuesta de Stephan.
Información Adicional :
Si escribo en cambio (del segundo buzón de mensajes):
MessageBox.Show((TaskScheduler.Current == TaskScheduler.Default).ToString());
Sí es true . Pero por qué ?
Las razones de la confusión son estas:
- La interfaz de usuario no tiene un
TaskScheduler"especial". El caso predeterminado para el código que se ejecuta en el subproceso de la interfaz de usuario es queTaskScheduler.CurrentalmacenaThreadPoolTaskSchedulerySynchronizationContext.CurrentalmacenaWindowsFormsSynchronizationContext(o el relevante en otras aplicaciones de IU) -
ThreadPoolTaskSchedulerenTaskScheduler.Currentno significa necesariamente que se esté utilizandoTaskSchedulerpara ejecutar el fragmento de código actual. También significaTaskSchdeuler.Current == TaskScheduler.Defaulty por lo tanto "no se está utilizandoTaskScheduler" . -
TaskScheduler.FromCurrentSynchronizationContext()no devuelve unTaskScheduler"acutal". Devuelve un "proxy" que publica tareas directamente en elSynchronizationContextcapturado.
Entonces, si ejecuta su prueba antes de comenzar la tarea (o en cualquier otro lugar) obtendrá el mismo resultado que después de la espera:
MessageBox.Show(TaskScheduler.Current == TaskScheduler.FromCurrentSynchronizationContext()); // False
Debido a que TaskScheduler.Current es ThreadPoolTaskScheduler y TaskScheduler.FromCurrentSynchronizationContext() devuelve un SynchronizationContextTaskScheduler .
Este es el flujo de tu ejemplo:
- Crea un nuevo
SynchronizationContextTaskSchedulerpartir delSynchronizationContextTaskSchedulerde la interfaz de usuario (es decir,WindowsFormsSynchronizationContext). - Programe la tarea que cree utilizando
Task.Factory.StartNewen eseTaskScheduler. Como solo es un "proxy", el delegado lo publica en elWindowsFormsSynchronizationContextque lo invoca en el subproceso de la interfaz de usuario. - La parte síncrona (la parte antes de la primera espera) de ese método se ejecuta en el subproceso de la interfaz de usuario, mientras se asocia con el
SynchronizationContextTaskScheduler. - El método llega a la espera y se "suspende" al capturar ese
WindowsFormsSynchronizationContext. - Cuando se reanuda la continuación después de la espera, se publica en ese
WindowsFormsSynchronizationContexty no enSynchronizationContextTaskSchedulerya queSynchronizationContexts tiene prioridad ( esto se puede ver enTask.SetContinuationForAwait). A continuación, se ejecuta regularmente en el subproceso de la interfaz de usuario, sin ningúnTaskScheduler"especial",TaskSchedulerlo queTaskScheduler.Current == TaskScheduler.Default.
Por lo tanto, la tarea creada se ejecuta en el proxy TaskScheduler que utiliza el SynchronizationContext pero la continuación después de la espera se publica en ese SynchronizationContext y no el TaskScheduler .