System.Threading.Tasks: limite el número de tareas simultáneas

Acabo de empezar a ver la nueva bondad de “System.Threading.Tasks” en .Net 4.0, y me gustaría saber si existe alguna comstackción en apoyo para limitar el número de tareas concurrentes que se ejecutan a la vez, o si esto debería ser manejado manualmente.

EG: Si necesito llamar a un método de cálculo 100 veces, ¿hay alguna manera de configurar 100 Tareas, pero solo 5 ejecutar simultáneamente? La respuesta puede ser simplemente crear 5 tareas, llamar a Task.WaitAny y crear una nueva tarea a medida que finaliza cada una de ellas. Solo quiero asegurarme de no perderme ningún truco si hay una mejor manera de hacerlo.

Básicamente, ¿existe una forma integrada de hacer esto?

Dim taskArray() = {New Task(Function() DoComputation1()), New Task(Function() DoComputation2()), ... New Task(Function() DoComputation100())} Dim maxConcurrentThreads As Integer = 5 RunAllTasks(taskArray, maxConcurrentThreads) 

Gracias por cualquier ayuda.

Sé que esto tiene casi un año, pero encontré una forma mucho más fácil de lograr esto, así que pensé en compartir:

 Dim actionsArray() As Action = new Action(){ New Action(Sub() DoComputation1()), New Action(Sub() DoComputation2()), ... New Action(Sub() DoComputation100()) } System.Threading.Tasks.Parallel.Invoke(New Tasks.ParallelOptions() With {.MaxDegreeOfParallelism = 5}, actionsArray) 

Voila!

Sé que este es un hilo viejo, pero solo quería compartir mi solución a este problema: usar semáforos.

(Esto está en C #)

 private void RunAllActions(IEnumerable actions, int maxConcurrency) { using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency)) { foreach(Action action in actions) { Task.Factory.StartNew(() => { concurrencySemaphore.Wait(); try { action(); } finally { concurrencySemaphore.Release(); } }); } } } 

Una solución podría ser echar un vistazo al código prefabricado de Microsoft aquí .

La descripción es la siguiente: “Proporciona un progtwigdor de tareas que garantiza un nivel máximo de concurrencia mientras se ejecuta en la parte superior del ThreadPool.”, Y en la medida en que he podido probarlo, parece ser el truco, de la misma manera que la propiedad MaxDegreeOfParallelism en ParallelOptions.

C # equivalente a la muestra proporcionada por James

 Action[] actionsArray = new Action[] { new Action(() => DoComputation1()), new Action(() => DoComputation2()), //... new Action(() => DoComputation100()) }; System.Threading.Tasks.Parallel.Invoke(new Tasks.ParallelOptions {MaxDegreeOfParallelism = 5 }, actionsArray) 

Respuesta corta: si lo que quiere es limitar el número de tareas de los trabajadores para que no saturen su servicio web, entonces creo que su enfoque es correcto.

Respuesta larga: el nuevo motor System.Threading.Tasks en .NET 4.0 se ejecuta en .NET ThreadPool. Dado que solo hay un ThreadPool por proceso, el valor predeterminado es un máximo de 250 subprocesos de trabajo. Por lo tanto, si configurara el número máximo de subprocesos de ThreadPool en un número más modesto, es posible que pueda reducir el número de subprocesos que se ejecutan simultáneamente y, por lo tanto, las tareas que utilizan la ThreadPool.SetMaxThreads (...) .

SIN EMBARGO, tenga en cuenta que es posible que no esté solo en la utilización de ThreadPool, ya que muchas otras clases que utilice también pueden poner artículos en cola en ThreadPool. Por lo tanto, hay una buena posibilidad de que acabes paralizando el rest de tu aplicación haciendo esto. También tenga en cuenta que debido a que ThreadPool emplea un algoritmo para optimizar el uso de los núcleos subyacentes de una máquina determinada, limitar el número de subprocesos que el subproceso puede hacer cola a un número arbitrariamente bajo puede dar como resultado algunos problemas de rendimiento bastante catastróficos.

De nuevo, si desea ejecutar un pequeño número de tareas / hilos de trabajo para ejercitar alguna tarea, solo crear una pequeña cantidad de tareas (frente a 100) es el mejor enfoque.

La publicación de mi blog muestra cómo hacer esto con Tareas y con Acciones, y proporciona un ejemplo de proyecto que puede descargar y ejecutar para ver ambos en acción.

Con acciones

Si usa Actions, puede usar la función incorporada .Net Parallel.Invoke. Aquí lo limitamos a ejecutar como máximo 5 hilos en paralelo.

 var listOfActions = new List(); for (int i = 0; i < 100; i++) { // Note that we create the Action here, but do not start it. listOfActions.Add(() => DoSomething()); } var options = new ParallelOptions {MaxDegreeOfParallelism = 5}; Parallel.Invoke(options, listOfActions.ToArray()); 

Con tareas

Sin embargo, dado que está utilizando tareas aquí, no hay una función incorporada. Sin embargo, puede usar el que brindo en mi blog.

  ///  /// Starts the given tasks and waits for them to complete. This will run, at most, the specified number of tasks in parallel. /// NOTE: If one of the given tasks has already been started, an exception will be thrown. ///  /// The tasks to run. /// The maximum number of tasks to run in parallel. /// The cancellation token. public static void StartAndWaitAllThrottled(IEnumerable tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = new CancellationToken()) { StartAndWaitAllThrottled(tasksToRun, maxTasksToRunInParallel, -1, cancellationToken); } ///  /// Starts the given tasks and waits for them to complete. This will run, at most, the specified number of tasks in parallel. /// NOTE: If one of the given tasks has already been started, an exception will be thrown. ///  /// The tasks to run. /// The maximum number of tasks to run in parallel. /// The maximum milliseconds we should allow the max tasks to run in parallel before allowing another task to start. Specify -1 to wait indefinitely. /// The cancellation token. public static void StartAndWaitAllThrottled(IEnumerable tasksToRun, int maxTasksToRunInParallel, int timeoutInMilliseconds, CancellationToken cancellationToken = new CancellationToken()) { // Convert to a list of tasks so that we don't enumerate over it multiple times needlessly. var tasks = tasksToRun.ToList(); using (var throttler = new SemaphoreSlim(maxTasksToRunInParallel)) { var postTaskTasks = new List(); // 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'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 luego, al crear su lista de tareas y llamar a la función para que se ejecuten, con un máximo de 5 simultáneas a la vez, puede hacer esto:

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

No se parece a eso, aunque podría crear una subclase de TaskScheduler que implemente dicho comportamiento.

Si su progtwig utiliza servicios web, el número de conexiones simultáneas estará limitado a la propiedad ServicePointManager.DefaultConnectionLimit . Si desea 5 conexiones simultáneas, no es suficiente usar la solución de Arrow_Raider. También debe boost ServicePointManager.DefaultConnectionLimit porque es solo 2 por defecto.