multithreading - method - Diferencia entre TPL y async/await(manejo de hilos)
threadpool c# ejemplo (2)
Creo que el TPL (TaskFactory.Startnew) funciona de manera similar a ThreadPool.QueueUserWorkItem, ya que pone en cola el trabajo en un subproceso en el grupo de subprocesos.
Por lo que he estado leyendo, parece que async / await solo "a veces" crea un nuevo hilo.
En realidad, nunca lo hace. Si quieres multihilo, debes implementarlo tú mismo. Hay un nuevo método Task.Run
que es solo una forma abreviada de Task.Factory.StartNew
, y es probablemente la forma más común de iniciar una tarea en el grupo de subprocesos.
Si estuviera trabajando con puertos de finalización de E / S, puedo ver que no tiene que crear un nuevo subproceso, pero de lo contrario pensaría que tendría que hacerlo.
Bingo. Así que los métodos como Stream.ReadAsync
realidad crearán un contenedor de Task
alrededor de un IOCP (si el Stream
tiene un IOCP).
También puede crear algunas "tareas" que no sean de E / S ni CPU. Un ejemplo simple es Task.Delay
, que devuelve una tarea que se completa después de algún período de tiempo.
Lo bueno de async
/ await
es que puede poner en cola algunos trabajos en el grupo de subprocesos (por ejemplo, Task.Run
), realizar alguna operación vinculada a E / S (por ejemplo, Stream.ReadAsync
) y realizar otra operación (por ejemplo, Task.Delay
) ... y son todas tareas! Pueden Task.WhenAll
o usarse en combinaciones como Task.WhenAll
.
Se puede await
cualquier método que devuelva Task
, no tiene que ser un método async
. Por Task.Delay
tanto, las Task.Delay
y I / O-bound solo usan TaskCompletionSource
para crear y completar una tarea; lo único que se hace en el grupo de subprocesos es la finalización real de la tarea cuando ocurre el evento (tiempo de espera, finalización de E / S, etc.).
Supongo que mi comprensión de FromCurrentSynchronizationContext siempre fue un poco borrosa también. Siempre a través de él fue, en esencia, el hilo de la interfaz de usuario.
Escribí un artículo en SynchronizationContext
. La mayoría de las veces, SynchronizationContext.Current
:
- es un contexto de interfaz de usuario si el subproceso actual es un subproceso de interfaz de usuario.
- es un contexto de solicitud ASP.NET si el subproceso actual está atendiendo una solicitud ASP.NET.
- es un contexto de agrupación de hilos de lo contrario.
Cualquier hilo puede establecer su propio SynchronizationContext
, por lo que hay excepciones a las reglas anteriores.
Tenga en cuenta que el operador de Task
predeterminado programará el resto del método async
en el SynchronizationContext
actual si no es nulo ; De lo contrario, va en el TaskScheduler
actual. Esto no es tan importante hoy, pero en el futuro será una distinción importante.
Escribí mi propia introducción de async
/ await
en mi blog, y Stephen Toub recientemente publicó una excelente FAQ de async
/ await
.
Con respecto a "concurrencia" frente a "multihilo", consulte esta pregunta SO relacionada . Yo diría que async
permite la concurrencia, que puede o no ser multiproceso. Es fácil de usar await Task.WhenAll
o await Task.WhenAny
para hacer un procesamiento concurrente, y a menos que uses explícitamente el grupo de hilos (ej., Task.Run
o ConfigureAwait(false)
), entonces puedes tener múltiples operaciones concurrentes en progreso al mismo tiempo (p. ej., varias E / S u otros tipos como Delay
), y no hay un hilo necesario para ellos. Utilizo el término "concurrencia de un solo hilo" para este tipo de escenario, aunque en un host ASP.NET, realmente puede terminar con "concurrencia de cero hilos". Que es muy dulce.
Tratando de entender la diferencia entre TPL y async
/ await
cuando se trata de la creación de hilos.
Creo que el TPL ( TaskFactory.StartNew
) funciona de manera similar a ThreadPool.QueueUserWorkItem
que ThreadPool.QueueUserWorkItem
en cola el trabajo en un subproceso en el grupo de subprocesos. Eso es, por supuesto, a menos que use TaskCreationOptions.LongRunning
que crea un nuevo hilo.
Pensé que async
/ await
funcionaría de manera similar:
TPL:
Factory.StartNew( () => DoSomeAsyncWork() )
.ContinueWith(
(antecedent) => {
DoSomeWorkAfter();
},TaskScheduler.FromCurrentSynchronizationContext());
Async
/ Await
:
await DoSomeAsyncWork();
DoSomeWorkAfter();
sería idéntico. Por lo que he estado leyendo, parece que async
/ await
solo "a veces" crea un nuevo hilo. Entonces, ¿cuándo crea un nuevo hilo y cuándo no crea un nuevo hilo? Si estuviera trabajando con puertos de finalización de E / S, puedo ver que no tiene que crear un nuevo subproceso, pero de lo contrario creo que tendría que hacerlo. Supongo que mi comprensión de FromCurrentSynchronizationContext
siempre fue un poco borrosa también. Siempre a través de él fue, en esencia, el hilo de la interfaz de usuario.
async / await básicamente simplifica los métodos ContinueWith
(Continuaciones en estilo de paso de continuación )
No introduce la concurrencia: todavía tiene que hacerlo usted mismo (o use la versión Async de un método de marco).
Entonces, la versión C # 5 sería:
await Task.Run( () => DoSomeAsyncWork() );
DoSomeWorkAfter();