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.Current
es lo mismo queTaskScheduler.Default
Cual es verdad . Ya lo vi here :
TaskScheduler.Default
- Devuelve una instancia de
ThreadPoolTaskScheduler
TaskScheduler.Current
- Si se llama desde dentro de una tarea en ejecución, se devolverá el
TaskScheduler
deTaskScheduler
de 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.Current
almacenaThreadPoolTaskScheduler
ySynchronizationContext.Current
almacenaWindowsFormsSynchronizationContext
(o el relevante en otras aplicaciones de IU) -
ThreadPoolTaskScheduler
enTaskScheduler.Current
no significa necesariamente que se esté utilizandoTaskScheduler
para ejecutar el fragmento de código actual. También significaTaskSchdeuler.Current == TaskScheduler.Default
y por lo tanto "no se está utilizandoTaskScheduler
" . -
TaskScheduler.FromCurrentSynchronizationContext()
no devuelve unTaskScheduler
"acutal". Devuelve un "proxy" que publica tareas directamente en elSynchronizationContext
capturado.
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
SynchronizationContextTaskScheduler
partir delSynchronizationContextTaskScheduler
de la interfaz de usuario (es decir,WindowsFormsSynchronizationContext
). - Programe la tarea que cree utilizando
Task.Factory.StartNew
en eseTaskScheduler
. Como solo es un "proxy", el delegado lo publica en elWindowsFormsSynchronizationContext
que 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
WindowsFormsSynchronizationContext
y no enSynchronizationContextTaskScheduler
ya queSynchronizationContext
s 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",TaskScheduler
lo 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
.