Diferencias entre tareas y subprocesos [duplicado]
Hay dos clases disponibles en .NET: Task
y Thread
.
- ¿Cuál es la diferencia entre esas clases?
- ¿ Cuándo es mejor utilizar
Thread
overTask
(y viceversa)?
Thread
es un concepto de nivel inferior: si está iniciando un subproceso directamente, sabrá que será un subproceso separado, en lugar de ejecutarse en el grupo de subprocesos, etc.
Task
Sin embargo, es más que una simple abstracción de "dónde ejecutar algún código": en realidad es solo "la promesa de un resultado en el futuro". Así como algunos ejemplos diferentes:
Task.Delay
no necesita ningún tiempo de CPU real; es como configurar un temporizador para que suene en el futuro- Una tarea devuelta por
WebClient.DownloadStringTaskAsync
no consumirá mucho tiempo de CPU a nivel local; representa un resultado que probablemente pasará la mayor parte de su tiempo en latencia de red o trabajo remoto (en el servidor web) - Una tarea devuelta por
Task.Run()
realmente dice "Quiero que ejecutes este código por separado"; el hilo exacto en el que se ejecuta ese código depende de varios factores.
Tenga en cuenta que la Task<T>
abstracción es fundamental para la compatibilidad asíncrona en C# 5.
En general, le recomiendo que utilice la abstracción de nivel superior siempre que pueda: en el código C# moderno, rara vez necesitará iniciar explícitamente su propio hilo.
Generalmente escuchas que Tarea es un concepto de nivel superior a hilo ... y eso es lo que significa esta frase:
No puede usar Abort/ThreadAbortedException, debe admitir el evento de cancelación en su "código comercial" probando periódicamente el
token.IsCancellationRequested
indicador (también evite conexiones largas o sin tiempo de espera, por ejemplo, a la base de datos; de lo contrario, nunca tendrá la oportunidad de probar este indicador). Por la misma razón,Thread.Sleep(delay)
la llamada debe reemplazarse conTask.Delay(delay, token)
la llamada (pasar el token al interior para tener la posibilidad de interrumpir el retraso).No hay funcionalidades de subprocesos
Suspend
ni métodos con tareas. La instancia de tarea tampoco se puede reutilizar .Resume
Pero obtienes dos nuevas herramientas:
a) continuaciones
// continuation with ContinueWhenAll - execute the delegate, when ALL // tasks[] had been finished; other option is ContinueWhenAny Task.Factory.ContinueWhenAll( tasks, () => { int answer = tasks[0].Result + tasks[1].Result; Console.WriteLine("The answer is {0}", answer); } );
b) tareas anidadas/secundarias
//StartNew - starts task immediately, parent ends whith child var parent = Task.Factory.StartNew (() => { var child = Task.Factory.StartNew(() => { //... }); }, TaskCreationOptions.AttachedToParent );
Por lo tanto, el subproceso del sistema está completamente oculto a la tarea, pero aún así el código de la tarea se ejecuta en el subproceso concreto del sistema. Los subprocesos del sistema son recursos para las tareas y, por supuesto, todavía hay un grupo de subprocesos bajo el capó de la ejecución paralela de la tarea. Puede haber diferentes estrategias para que los subprocesos ejecuten nuevas tareas. Otro recurso compartido, TaskScheduler, se preocupa por ello. Algunos problemas que resuelve TaskScheduler 1) prefieren ejecutar la tarea y su continuación en el mismo subproceso minimizando el costo de cambio, también conocido como ejecución en línea ) 2) prefieren ejecutar las tareas en el orden en que se iniciaron, también conocido como PreferFairness 3) distribución más efectiva de tareas entre subprocesos inactivos dependiendo del "conocimiento previo de la actividad de las tareas", también conocido como robo de trabajo . Importante: en general, "asíncrono" no es lo mismo que "paralelo". Al jugar con las opciones de TaskScheduler, puede configurar que las tareas asíncronas se ejecuten en un hilo de forma sincrónica. Para expresar la ejecución de código paralelo se podrían utilizar abstracciones superiores (que las Tareas):
Parallel.ForEach
,PLINQ
,Dataflow
.Las tareas están integradas con las funciones async/await de C#, también conocidas como Promise Model , por ejemplo, allí
requestButton.Clicked += async (o, e) => ProcessResponce(await client.RequestAsync(e.ResourceName));
la ejecuciónclient.RequestAsync
no bloqueará el subproceso de la interfaz de usuario. Importante: bajo el capó,Clicked
la llamada delegado es absolutamente regular (todos los subprocesos los realiza el compilador).
Eso es suficiente para tomar una decisión. Si necesita admitir la funcionalidad Cancelar para llamar a una API heredada que tiende a bloquearse (por ejemplo, una conexión sin tiempo de espera) y, en este caso, admite Thread.Abort(), o si está creando cálculos en segundo plano de subprocesos múltiples y desea optimizar el cambio entre subprocesos usando Suspender/Reanudar , eso significa administrar la ejecución paralela manualmente; quédese con Thread. De lo contrario, vaya a Tareas porque le permitirán manipular fácilmente grupos de ellas, están integradas en el lenguaje y hacen que los desarrolladores sean más productivos: Biblioteca paralela de tareas (TPL) .