Sistema.Enhebrando.Tareas-Limitar el número de tareas simultáneas


Acabo de empezar a mirar el nuevo "Sistema.Enhebrando.Tareas " bondad en. Net 4.0, y le gustaría saber si hay alguna construcción en el apoyo para limitar el número de tareas simultáneas que se ejecutan a la vez, o si esto debe ser manejado manualmente.

Por ejemplo: Si necesito llamar a un método de cálculo 100 veces, ¿hay una manera de configurar 100 Tareas, pero tener solo 5 ejecutadas simultáneamente? La respuesta puede ser crear 5 tareas, llamar Tarea.WaitAny, y crear una nueva tarea como cada uno anterior terminar. Solo quiero asegurarme de que no me estoy perdiendo un truco si hay una mejor manera de hacer esto.

Básicamente, hay una forma incorporada 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.

Author: James, 2010-05-24

8 answers

Sé que esto es casi un año de edad, pero he encontrado una manera 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!

 44
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-21 19:49:29

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<Action> actions, int maxConcurrency)
{
    using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
    {
        foreach(Action action in actions)
        {
            Task.Factory.StartNew(() =>
            {
                concurrencySemaphore.Wait();
                try
                {
                    action();
                }
                finally
                {
                    concurrencySemaphore.Release();
                }
            });
        }
    }
}
 26
Author: Arrow_Raider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-12 18:31:04

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

La descripción es así: "Proporciona un programador de tareas que garantiza un nivel máximo de concurrencia mientras se ejecuta en la parte superior del ThreadPool.", y por lo que he podido probar parece hacer el truco, de la misma manera que la propiedad MaxDegreeOfParallelism en ParallelOptions.

 7
Author: persistent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-12 07:34:38

C# equivalente de 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)
 6
Author: Nigrimmist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-25 10:48:56

Respuesta corta: Si lo que desea es limitar el número de tareas de los trabajadores para que no saturen su servicio web, entonces creo que su enfoque está bien.

Respuesta Larga: El nuevo Sistema.Enhebrando.El motor de tareas en. NET 4.0 se ejecuta sobre el ThreadPool. NET. Dado que solo hay un ThreadPool por proceso y el valor predeterminado es un máximo de 250 subprocesos de trabajo. Por lo tanto, si establece el número máximo de hilos del ThreadPool en un número más modesto, puede ser capaz de reducir el número de subprocesos que se ejecutan simultáneamente y, por lo tanto, tareas utilizando la API ThreadPool.SetMaxThreads (...).

SIN embargo, tenga en cuenta que es posible que no esté solo al utilizar el ThreadPool, ya que muchas otras clases que utilice también pueden poner en cola elementos al ThreadPool. Por lo tanto, hay una buena probabilidad de que pueda terminar paralizando el resto de su aplicación al hacer esto. También tenga en cuenta que debido a que el ThreadPool emplea un algoritmo para optimizar su uso de los núcleos subyacentes de una máquina dada, lo que limita el número de hilos el threadpool puede hacer cola a un número arbitrariamente bajo puede resultar en algunos problemas de rendimiento bastante catastróficos.

Nuevamente, si desea ejecutar un pequeño número de tareas/subprocesos de trabajo para ejercer alguna tarea, entonces solo crear un pequeño número de tareas (vs.100) es el mejor enfoque.

 3
Author: Rich Turner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-26 21:46:36

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

Con Acciones

Si usa Acciones, puede usar el paralelo.Net incorporado.Invocar función. Aquí lo limitamos a ejecutar como máximo 5 hilos en paralelo.

var listOfActions = new List<Action>();
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

Dado que está usando Tareas aquí, sin embargo, no hay una función incorporada. Sin embargo, usted puede utilizar el que yo proporcione 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 luego crear su lista de Tareas y llamar a la función para que se ejecuten, con digamos un máximo de 5 simultáneas a la vez, podría hacer esto:

var listOfTasks = new List<Task>();
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);
 3
Author: deadlydog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-29 07:44:25

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

 0
Author: Justin Ethier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-13 07:44:44

Si su programa utiliza servicios web, el número de conexiones simultáneas se limitará a la propiedad ServicePointManager.DefaultConnectionLimit. Si desea 5 conexiones simultáneas no es suficiente utilizar la solución Arrow_Raider. También debe aumentar ServicePointManager.DefaultConnectionLimit porque es solo 2 por defecto.

 -3
Author: user1341188,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-18 11:53:29