web-dev-qa-db-fra.com

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 déclenche 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 premier candidat. Voici ce que les choses deviennent difficiles. J'aurai peut-être besoin de déclencher 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 la MaxThreads sur la 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 la 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 ce que je fais. 

Le cœur de ma question est donc de savoir 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 supprimé complètement 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 lock, sauf que le Semaphore n'a pas de "propriétaire" - il est agnostique au fil. Tout thread peut appeler Release sur une 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. Une 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();
        }
    }
}
36
jason baisden

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 d'un problème préalable. 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'accès à 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 objet 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’on lui donne 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'entre eux augmente le compteur.

Cette solution n'est pas parfaite. 


Si vous voulez quelque chose d'un peu plus propre et plus efficace, je vous recommande d'utiliser une approche BlockingQueue dans laquelle vous mettez en file d'attente le travail que vous souhaitez effectuer dans un objet global Blocking Queue.

En attendant, vous avez trois threads (que vous avez créés - pas dans le pool de threads), qui extraient le travail de la file d'attente. Ce n’est pas si difficile à installer, c’est très simple et rapide.

Exemples:

30
Michael Haren

C'est une classe statique comme toute autre classe, 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 brillante idée de créer 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.

0
jalf