System.Threading.Tasks – Limite le nombre de tâches simultanées

Je viens tout juste de commencer à examiner les nouveaux bienfaits de “System.Threading.Tasks” dans .Net 4.0, et je voudrais savoir s’il existe un support pour limiter le nombre de tâches simultanées qui s’exécutent simultanément ou si cela devrait se produire. être manipulé manuellement

EG: Si j’ai besoin d’appeler une méthode de calcul 100 fois, y a-t-il un moyen de configurer 100 tâches, mais que 5 seulement s’exécutent simultanément? La réponse peut être simplement de créer 5 tâches, d’appeler Task.WaitAny et de créer une nouvelle tâche à la fin de chaque tâche précédente. Je veux juste m’assurer de ne pas rater un tour s’il y a une meilleure façon de le faire.

Fondamentalement, existe-t-il un moyen intégré de le faire:

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

Merci pour toute aide.

Je sais que c’est presque un an, mais j’ai trouvé un moyen beaucoup plus facile d’y parvenir, alors j’ai pensé partager:

 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!

Je sais que c’est un vieux sujet, mais je voulais juste partager ma solution à ce problème: utiliser des sémaphores.

(Ceci 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(); } }); } } } 

Une solution pourrait être de regarder le code pré-créé de Microsoft ici .

La description se présente comme suit: “Fournit un planificateur de tâches qui garantit un niveau de simultanéité maximal lors de l’exécution du ThreadPool.” Et, autant que j’ai pu le tester, il semble faire l’affaire, de la même manière que la propriété MaxDegreeOfParallelism dans ParallelOptions.

C # équivalent de l’échantillon fourni par 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) 

Réponse courte: Si vous voulez limiter le nombre de tâches des employés pour qu’elles ne saturent pas votre service Web, je pense que votre approche est la bonne.

Réponse longue: Le nouveau moteur System.Threading.Tasks dans .NET 4.0 s’exécute sur le ThreadPool .NET. Comme il n’y a qu’un seul ThreadPool par processus et que la valeur par défaut est de 250 threads de travail. Par conséquent, si vous définissez le nombre maximal de threads de ThreadPool sur un nombre plus modeste, vous pourrez peut-être réduire le nombre de threads exécutés simultanément, et donc les tâches à l’aide de l’ ThreadPool.SetMaxThreads (...) .

Cependant, notez que vous pourriez bien ne pas être seul à utiliser le ThreadPool, car de nombreuses autres classes que vous utilisez peuvent également mettre en queue des éléments sur le ThreadPool. Par conséquent, il est fort possible que vous finissiez par endommager le rest de votre application. Notez également que, comme ThreadPool utilise un algorithme pour optimiser son utilisation des cœurs sous-jacents d’une machine donnée, limiter le nombre de threads que le pool de threads peut mettre en queue à un nombre arbitrairement faible peut entraîner des problèmes de performances catastrophiques.

Encore une fois, si vous souhaitez exécuter un petit nombre de tâches / threads de travail pour effectuer certaines tâches, la meilleure approche consiste à créer un petit nombre de tâches (contre 100).

Mon article de blog montre comment procéder à la fois avec Tasks et avec Actions et fournit un exemple de projet que vous pouvez télécharger et exécuter pour voir les deux en action.

Avec des actions

Si vous utilisez Actions, vous pouvez utiliser la fonction .Net Parallel.Invoke intégrée. Ici, nous nous limitons à exécuter au maximum 5 threads en parallèle.

 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()); 

Avec des tâches

Cependant, comme vous utilisez des tâches ici, il n’y a pas de fonction intégrée. Cependant, vous pouvez utiliser celui que je fournis sur mon 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); } } 

Et puis, en créant votre liste de tâches et en appelant la fonction pour les faire fonctionner, vous pouvez le faire avec: disons un maximum de 5 simultanées à la fois:

 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); 

Cela ne lui ressemble pas, bien que vous puissiez créer une sous-classe de TaskScheduler qui implémente un tel comportement.

Si votre programme utilise des services Web, le nombre de connexions simultanées sera limité à la propriété ServicePointManager.DefaultConnectionLimit . Si vous souhaitez 5 connexions simultanées, il ne suffit pas d’utiliser la solution d’Arrow_Raider. Vous devez également augmenter ServicePointManager.DefaultConnectionLimit car il n’est que 2 par défaut.