2012-06-17 10 views
7

Estoy usando el TPL para agregar nuevas tareas al grupo de subprocesos del sistema usando la función Task.Factory.StartNew(). El único problema es que estoy agregando muchos subprocesos y creo que está creando demasiados para que los maneje mi procesador. ¿Hay alguna manera de establecer un número máximo de subprocesos en este grupo de subprocesos?Uso de TPL cómo configuro un tamaño máximo de subprocesos

+3

No se limite a pensar , averigua si es realmente cierto. – svick

+0

¿Algún código de muestras? –

Respuesta

12

El valor predeterminado TaskScheduler (obtenido de TaskScheduler.Default) es del tipo (clase interna) ThreadPoolTaskScheduler. Esta implementación utiliza la clase ThreadPool para cola de tareas (si Task no se crea con TaskCreationOptions.LongRunning, en este caso se crea un nuevo subproceso para cada tarea).

lo tanto, si desea limitar el # de subprocesos disponibles para Task objetos creados a través de new Task(() => Console.WriteLine("In task")), puede limitar los hilos disponibles en el threadpool mundial como esto:

// Limit threadpool size 
int workerThreads, completionPortThreads; 
ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); 
workerThreads = 32; 
ThreadPool.SetMaxThreads(workerThreads, completionPortThreads); 

La llamada a ThreadPool.GetMaxThreads() se hace para evite reducir el completionPortThreads.

Tenga en cuenta que esto puede ser una mala idea - ya que todas las tareas sin un planificador especificado, y cualquier número de otras clases de uso de la ThreadPool defecto, el ajuste del tamaño demasiado bajo puede causar efectos secundarios: El hambre, etc.

3

Primero debe investigar sus problemas de rendimiento. Hay varias cuestiones que pueden resultar en una menor utilización:

  • Programación de tareas de ejecución prolongada sin la opción LongRunningTask
  • intentar abrir más de dos conexiones simultáneas a la misma dirección web
  • bloqueo para el acceso a la mismo recurso
  • intentar acceder al hilo de interfaz de usuario mediante Invoke() desde varios subprocesos

En cualquier caso, usted tiene un problema de escalabilidad que no se puede tratar simplemente reduciendo el número de tareas simultáneas. Su programa puede ejecutarse en una máquina de dos, cuatro u ocho núcleos en el futuro. Limitar el número de tareas programadas simplemente conducirá al desperdicio de recursos de la CPU.

+1

Gracias por el voto a favor, ¿me importa explicarlo? Un error común es que las personas asuman que las Tareas son iguales a los hilos y tratan de limitar el número de tareas, lo que resulta en problemas de rendimiento aún mayores. –

-1

Por lo general, el planificador de TPL debe hacer un buen trabajo al elegir cuántas tareas ejecutar simultáneamente, pero si realmente desea tener control sobre él My blog post muestra cómo hacerlo con tareas y con acciones, y proporciona un ejemplo de proyecto que puede descargar y corre para ver a ambos en acción.

Un ejemplo de cuándo es posible que desee limitar explícitamente cuántas tareas se ejecutan simultáneamente es cuando está llamando a sus propios servicios y no desea sobrecargar su servidor.

Por lo que está describiendo, parece que puede beneficiarse más asegurarse de que está utilizando async/await con sus tareas para evitar el consumo innecesario de hilo. Sin embargo, esto dependerá de si está realizando un trabajo vinculado a la CPU o un trabajo vinculado a la IO en sus tareas. Si está vinculado a IO, entonces puede beneficiarse enormemente del uso de async/await.

Independientemente, usted preguntó cómo puede limitar el número de tareas que se ejecutan simultáneamente, así que aquí hay un código para mostrar cómo hacerlo con acciones y tareas.

Con acciones

Si Uso de acciones, puede utilizar la función incorporada en .Net Parallel.Invoke. Aquí lo limitamos a ejecutar como máximo 3 hilos en paralelo.

var listOfActions = new List<Action>(); 
for (int i = 0; i < 10; i++) 
{ 
    // Note that we create the Action here, but do not start it. 
    listOfActions.Add(() => DoSomething()); 
} 

var options = new ParallelOptions {MaxDegreeOfParallelism = 3}; 
Parallel.Invoke(options, listOfActions.ToArray()); 

con tareas

dado que está utilizando Tareas aquí, sin embargo, no hay ninguna función integrada. Sin embargo, puede usar el que brindo en mi blog.

/// <summary> 
    /// Starts the given tasks and waits for them to complete. This will run, at most, the specified number of tasks in parallel. 
    /// <para>NOTE: If one of the given tasks has already been started, an exception will be thrown.</para> 
    /// </summary> 
    /// <param name="tasksToRun">The tasks to run.</param> 
    /// <param name="maxTasksToRunInParallel">The maximum number of tasks to run in parallel.</param> 
    /// <param name="cancellationToken">The cancellation token.</param> 
    public static void StartAndWaitAllThrottled(IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = new CancellationToken()) 
    { 
     StartAndWaitAllThrottled(tasksToRun, maxTasksToRunInParallel, -1, cancellationToken); 
    } 

    /// <summary> 
    /// Starts the given tasks and waits for them to complete. This will run, at most, the specified number of tasks in parallel. 
    /// <para>NOTE: If one of the given tasks has already been started, an exception will be thrown.</para> 
    /// </summary> 
    /// <param name="tasksToRun">The tasks to run.</param> 
    /// <param name="maxTasksToRunInParallel">The maximum number of tasks to run in parallel.</param> 
    /// <param name="timeoutInMilliseconds">The maximum milliseconds we should allow the max tasks to run in parallel before allowing another task to start. Specify -1 to wait indefinitely.</param> 
    /// <param name="cancellationToken">The cancellation token.</param> 
    public static void StartAndWaitAllThrottled(IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, int timeoutInMilliseconds, CancellationToken cancellationToken = new CancellationToken()) 
    { 
     // Convert to a list of tasks so that we don&#39;t enumerate over it multiple times needlessly. 
     var tasks = tasksToRun.ToList(); 

     using (var throttler = new SemaphoreSlim(maxTasksToRunInParallel)) 
     { 
      var postTaskTasks = new List<Task>(); 

      // Have each task notify the throttler when it completes so that it decrements the number of tasks currently running. 
      tasks.ForEach(t => postTaskTasks.Add(t.ContinueWith(tsk => throttler.Release()))); 

      // Start running each task. 
      foreach (var task in tasks) 
      { 
       // Increment the number of tasks currently running and wait if too many are running. 
       throttler.Wait(timeoutInMilliseconds, cancellationToken); 

       cancellationToken.ThrowIfCancellationRequested(); 
       task.Start(); 
      } 

      // Wait for all of the provided tasks to complete. 
      // We wait on the list of "post" tasks instead of the original tasks, otherwise there is a potential race condition where the throttler&#39;s using block is exited before some Tasks have had their "post" action completed, which references the throttler, resulting in an exception due to accessing a disposed object. 
      Task.WaitAll(postTaskTasks.ToArray(), cancellationToken); 
     } 
    } 

Y a continuación, crear su lista de tareas y llamar a la función a que se ejecuten, con decir un máximo de 3 simultánea a la vez, usted puede hacer esto:

var listOfTasks = new List<Task>(); 
for (int i = 0; i < 10; i++) 
{ 
    var count = i; 
    // Note that we create the Task here, but do not start it. 
    listOfTasks.Add(new Task(() => Something())); 
} 
Tasks.StartAndWaitAllThrottled(listOfTasks, 3); 
Cuestiones relacionadas