c# - startnew - ¿Cuándo usar TaskCreationOptions.LongRunning?
task vs thread c# (4)
Me he preguntado esto durante bastante tiempo, pero nunca encontré la respuesta.
Entiendo que es una sugerencia para el programador de tareas donde se ejecutará la tarea, y que el programador de tareas puede (¿o hoy en día?) Decidir crear una instancia de subproceso que no sea de grupo de subprocesos para esa tarea.
Lo que no sé (y, sorprendentemente, no puedo encontrar en ninguna parte de Internet) es una "regla de oro" cuando se especifica una tarea como de larga duración. ¿Es un segundo largo? ¿30 segundos? ¿Un minuto? ¿5 minutos? ¿Tiene alguna relación con la cantidad de tareas que utiliza la aplicación? ¿Debería como programador realizar algunos cálculos con los # hilos en el grupo de subprocesos, cuántas Tareas creo, cuántas serán de larga duración al mismo tiempo y, en función de eso, decidir si utilizar una tarea de larga duración?
Espero aprender algo aquí.
cuándo especificar una tarea como de larga duración
Depende de lo que haga la tarea. Si la tarea contiene while(true) {...}
y vive hasta el cierre de la aplicación, entonces tiene sentido especificar LongRunning
. Si crea una tarea para poner en cola alguna operación y evita el bloqueo del subproceso actual, entonces realmente no le importa (no especifique nada).
Depende de lo que hagan otras tareas. No importa si ejecuta pocas tareas con o sin LongRunning
. Pero podría ser un problema crear miles de tareas, donde cada una demanda un nuevo hilo. O al contrario, puede experimentar un hambre de hilo sin especificarlo.
Una manera fácil de pensarlo es la siguiente: ¿prefiere que se ejecute una nueva tarea en un nuevo hilo o no le importa? Si es el primero, entonces use LongRunningOption
. Esto no significa que la tarea se ejecutará en otro hilo, solo es un buen criterio cuando tiene que especificarlo.
Por ejemplo, cuando se usa ContinueWith
, LongRunning
es opuesto a ExecuteSynchronously
(hay una check para evitar que se especifiquen). Si tiene varias continuaciones, entonces tal vez quiera evitar la sobrecarga de la cola y ejecutar una continuación específica en el mismo hilo o al contrario: no quiere que una de las continuaciones interfiera con otras y luego puede usar LongRunning
específicamente. Consulte este artículo (y this ) para obtener información acerca de ExecuteSynchronously
.
Enmendando la respuesta de Hans con la que estoy más de acuerdo.
La razón más importante para especificar LongRunning
es obtener una ejecución prácticamente garantizada y casi inmediata. No habrá ninguna espera para que el grupo de subprocesos emita un subproceso a su elemento de trabajo. Estoy diciendo "prácticamente" porque el sistema operativo es libre de no programar tu hilo. Pero obtendrás una parte de la CPU y normalmente no llevará mucho tiempo hasta que suceda.
Estás saltando frente a la cola especificando LongRunning
. No es necesario esperar a que el grupo de subprocesos emita 2 subprocesos por segundo si está bajo carga.
Por lo tanto, utilizaría LongRunning
para cosas que deben suceder, no necesariamente de la manera más eficiente, sino de manera oportuna y constante. Por ejemplo, algunos trabajos de interfaz de usuario, el bucle del juego, informes de progreso, ...
Iniciar y detener un subproceso cuesta en el orden de 1 ms de tiempo de CPU. Esto es mucho más que emitir elementos de trabajo de grupo de subprocesos. Simplemente comparé esto con 3M artículos emitidos y completados por segundo. El punto de referencia era bastante artificial, pero el orden de magnitud es correcto.
LongRunning
ha documentado que LongRunning
es una sugerencia, pero es absolutamente efectivo en la práctica. No hay heurística que tenga en cuenta tu insinuación. Se asume que es correcto.
Se puede cuantificar, el administrador de subprocesos agrega un subproceso adicional más allá del óptimo cuando los subprocesos tp existentes no se completan lo suficientemente pronto. Lo hace dos veces por segundo, hasta el máximo establecido por SetMaxThreads (). Que tiene un valor por defecto muy alto. Lo óptimo es el número de núcleos de procesador que la máquina tiene disponibles, 4 es típico. La ejecución de más subprocesos que los núcleos disponibles puede ser perjudicial debido a la sobrecarga de cambio de contexto.
Esto se basa en el supuesto de que estos subprocesos existentes no progresan porque no están ejecutando suficiente código. En otras palabras, bloquean en I / O o un bloqueo demasiado. Por lo tanto, dichos subprocesos no utilizan los núcleos de manera suficientemente eficiente y permitir que se ejecute un subproceso adicional es completamente apropiado para aumentar el uso del procesador y hacer más trabajo.
Por lo tanto, es "de larga duración" cuando el hilo tarda más de medio segundo. Tenga en cuenta que este es un tiempo muy largo, que equivale a aproximadamente 4 mil millones de instrucciones de procesador en una máquina de clase de escritorio moderna. A menos que esté ejecutando código pesado computacionalmente como el cálculo del valor de pi a un millón y medio de dígitos, ejecutando esas 4 mil millones de instrucciones, un hilo práctico solo puede tomar este tiempo cuando se bloquea con demasiada frecuencia. Lo que es muy común, algo así como una consulta de base de datos a menudo es lento y se ejecuta en un subproceso de trabajo y consume poca CPU.
De lo contrario, depende de usted verificar que la suposición que realizará el administrador de subprocesos sea correcta. La tarea debería llevar mucho tiempo porque no utiliza el procesador de manera eficiente. El Administrador de tareas es una forma sencilla de ver qué hacen los núcleos del procesador en su programa, aunque no le dirá exactamente qué código están ejecutando. Necesitará una prueba de unidad para ver el hilo ejecutándose de forma aislada. La última y única forma completamente precisa de decirle que usar LongRunning fue una opción adecuada es verificar que su aplicación realmente haga más trabajo.
Una tarea de larga ejecución es una que puede entrar en un estado de espera, bloquear el subproceso en el que se ejecuta, o una que requiere demasiado tiempo de CPU (volveremos a este).
Algunos pueden decir que esta definición es demasiado amplia, que muchas tareas durarían mucho tiempo, pero piénselo, aunque la espera esté limitada a un pequeño tiempo de espera, la tarea aún no está utilizando la CPU de manera efectiva. Si la cantidad de estas tareas aumenta, observará que no se escalan linealmente más allá de MinWorkerThreads (consulte ThreadPool.SetMinThreads
), la degradación es realmente mala.
El enfoque es cambiar todas las E / S (archivos, redes, bases de datos, etc.) para que sean asíncronas.
También hay tareas de larga ejecución debido a largos cálculos de CPU intensivos.
El enfoque consiste en diferir el cálculo, como insertar a la await Task.Yield()
en ciertos puntos, o preferiblemente diferir el cálculo explícitamente programando una tarea tras otra, cada una procesando un trozo de datos previamente dividido o procesando un búfer hasta un tiempo limitado límite.
El "demasiado tiempo" depende de usted decidir.
Cuando se encuentra en un entorno donde está compartiendo el grupo de subprocesos, cualquier momento es demasiado tiempo, debe elegir un valor razonable.
Por ejemplo, en ASP.NET bajo IIS, vea cuál es el tiempo promedio empleado por solicitud para las solicitudes más comunes. De manera similar, en un servicio donde se usa el grupo de hilos, por ejemplo, para procesar una cola de mensajes, tome el promedio por mensaje.
Más generalmente, "demasiado tiempo" es cuando los elementos de trabajo se ponen en cola más rápido de lo que se procesan. Puede haber ráfagas de trabajo, por lo que debe promediar esto con respecto a la unidad de tiempo que le importa, ya sea un segundo, un minuto, 10 minutos, etc. Cuando tenga un SLA, debe tener este intervalo definido en algún lugar.
Después de tener un valor razonable, debe ver, en la práctica, si está bien aumentarlo o si debe disminuirlo. Más a menudo, si puede aumentarla, es mejor que no la aumente, a menos que pueda ver una diferencia significativa en el rendimiento. "Significativo" significa que la cantidad de elementos procesados aumenta más que de manera lineal, por lo que si es lineal (o por debajo de lineal, puede suceder), no lo haga.
En mi experiencia, si tiene una tarea de ejecución larga por cualquiera de estas definiciones, normalmente estará mejor administrando su propio hilo o conjunto de hilos.