usar puede net method hebras example ejemplo ejecucion create como await async asp asincronos asincrona c# .net async-await task-parallel-library

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 que TaskScheduler.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 de TaskScheduler 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:

  1. 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 que TaskScheduler.Current almacena ThreadPoolTaskScheduler y SynchronizationContext.Current almacena WindowsFormsSynchronizationContext (o el relevante en otras aplicaciones de IU)
  2. ThreadPoolTaskScheduler en TaskScheduler.Current no significa necesariamente que se esté utilizando TaskScheduler para ejecutar el fragmento de código actual. También significa TaskSchdeuler.Current == TaskScheduler.Default y por lo tanto "no se está utilizando TaskScheduler " .
  3. TaskScheduler.FromCurrentSynchronizationContext() no devuelve un TaskScheduler "acutal". Devuelve un "proxy" que publica tareas directamente en el SynchronizationContext 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 del SynchronizationContextTaskScheduler de la interfaz de usuario (es decir, WindowsFormsSynchronizationContext ).
  • Programe la tarea que cree utilizando Task.Factory.StartNew en ese TaskScheduler . Como solo es un "proxy", el delegado lo publica en el WindowsFormsSynchronizationContext 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 en SynchronizationContextTaskScheduler ya que SynchronizationContext s tiene prioridad ( esto se puede ver en Task.SetContinuationForAwait ). A continuación, se ejecuta regularmente en el subproceso de la interfaz de usuario, sin ningún TaskScheduler "especial", TaskScheduler lo que TaskScheduler.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 .