Pool de threads C # limitant les threads

D’accord … J’ai donné au site une recherche honnête et j’ai lu de nombreux articles sur ce sujet. J’ai trouvé cette question: Code pour un pool de threads simple en C # particulièrement utile.

Cependant, comme il semble toujours, ce dont j’ai besoin varie légèrement.

J’ai examiné l’exemple MSDN et je l’ai adapté quelque peu à mes besoins. L’exemple auquel je me réfère est ici: http://msdn.microsoft.com/en-us/library/3dasc8as(VS.80,printer).aspx

Mon problème est le suivant. J’ai un ensemble de code assez simple qui charge une page Web via les classes HttpWebRequest et WebResponse et lit les résultats via un Stream . Je lance cette méthode dans un thread car elle devra être exécutée plusieurs fois. La méthode elle-même est assez courte, mais le nombre de fois qu’elle doit être déclenchée (avec des données variées pour chaque heure) varie. Cela peut aller de 1 à 200.

Tout ce que j’ai lu semble indiquer que la classe ThreadPool est le candidat principal. Voici ce que les choses deviennent difficiles. J’aurais peut-être besoin de lancer cette chose 100 fois, mais je ne peux avoir que 3 threads au maximum en cours d’exécution (pour cette tâche particulière).

J’ai essayé de définir les MaxThreads sur le ThreadPool via:

 ThreadPool.SetMaxThreads(3, 3); 

Je ne suis pas entièrement convaincu que cette approche fonctionne. De plus, je ne veux pas encombrer d’autres sites Web ou programmes exécutés sur le système sur lequel il sera exécuté. Donc, en limitant le nombre de threads sur le ThreadPool , puis-je être certain que cela ne concerne que mon code et mes threads?

L’exemple MSDN utilise l’approche du lecteur d’événements et appelle WaitHandle.WaitAll(doneEvents); c’est comme ça que je fais ça.

Le cœur de ma question est donc: comment garantir ou spécifier un nombre maximal de threads pouvant être exécutés pour leur code, alors que le code continue à exécuter plus de threads, les précédents finissant jusqu’à un point arbitraire? Est-ce que je m’y attaque de la bonne façon?

Cordialement,

Jason


D’accord, j’ai ajouté une approche de sémaphore et complètement supprimé le code ThreadPool . Cela semble assez simple. J’ai reçu mes informations sur: http://www.albahari.com/threading/part2.aspx

C’est cet exemple qui m’a montré comment:

[le texte ci-dessous est un copier / coller du site]

Un Semaphore avec une capacité de un est similaire à un Mutex ou à un lock , à la différence que le Semaphore n’a pas de “propriétaire” – il est agnostique en threads. N’importe quel thread peut appeler Release sur un Semaphore , tandis qu’avec Mutex et lock , seul le thread ayant obtenu la ressource peut la libérer.

Dans cet exemple, dix threads exécutent une boucle avec une instruction Sleep au milieu. Un Semaphore garantit que pas plus de trois threads peuvent exécuter cette instruction Sleep à la fois:

 class SemaphoreTest { static Semaphore s = new Semaphore(3, 3); // Available=3; Capacity=3 static void Main() { for (int i = 0; i < 10; i++) new Thread(Go).Start(); } static void Go() { while (true) { s.WaitOne(); Thread.Sleep(100); // Only 3 threads can get here at once s.Release(); } } } 

Remarque: si vous limitez le nombre à “3” afin de ne pas surcharger la machine qui exécute votre application, je m’assurerais qu’il s’agit tout d’abord d’un problème. Le pool de threads est censé gérer cela pour vous. D’un autre côté, si vous ne voulez pas surcharger une autre ressource, lisez la suite!


Vous ne pouvez pas gérer la taille du pool de threads (ou une grande partie de celui-ci).

Dans ce cas, j’utiliserais un sémaphore pour gérer l’access à votre ressource. Dans votre cas, votre ressource exécute la fonction Web, calcule un rapport, etc.

Pour ce faire, dans votre classe statique, créez un object sémaphore:

 System.Threading.Semaphore S = new System.Threading.Semaphore(3, 3); 

Ensuite, dans chaque fil, vous faites ceci:

 System.Threading.Semaphore S = new System.Threading.Semaphore(3, 3); try { // wait your turn (decrement) S.WaitOne(); // do your thing } finally { // release so others can go (increment) S.Release(); } 

Chaque thread bloquera sur le S.WaitOne () jusqu’à ce qu’il soit donné le signal de continuer. Une fois que S a été décrémenté 3 fois, tous les threads seront bloqués jusqu’à ce que l’un d’eux incrémente le compteur.

Cette solution n’est pas parfaite.


Si vous voulez quelque chose d’un peu plus propre et plus efficace, nous vous recommandons d’utiliser une approche BlockingQueue dans laquelle vous mettez en queue le travail que vous souhaitez effectuer dans un object global Blocking Queue.

Pendant ce temps, vous avez trois threads (que vous avez créés – pas dans le pool de threads), qui extraient le travail de la queue. Ce n’est pas si difficile à configurer et est très simple et rapide.

Exemples:

  • Meilleur exemple de queue de threading / meilleure pratique
  • Meilleure méthode pour obtenir des objects d’une BlockingQueue dans un programme simultané?

C’est une classe statique comme une autre, ce qui signifie que tout ce que vous faites avec elle affecte tous les autres threads du processus en cours. Cela n’affecte pas les autres processus.

Je considère cependant qu’il s’agit de l’un des plus gros défauts de conception de .NET. Qui a eu la shinye idée de rendre le pool de threads statique ? Comme votre exemple le montre, nous souhaitons souvent un pool de threads dédié à notre tâche, sans qu’il interfère avec des tâches non liées ailleurs dans le système.