2010-01-14 5 views
5

Dans mon application C#/NET 3.5 actuelle, j'ai une file d'attente de tâches (thread-safe) et j'ai 5 threads de travail qui doivent constamment chercher des tâches dans la file d'attente. Si une tâche est disponible, n'importe quel travailleur va la déquiler et prendre les mesures requises.Quelle est la méthode la plus efficace pour que les threads de travail attendent les tâches?

Ma classe de thread de travail est la suivante:

public class WorkerThread 
{ 
    //ConcurrentQueue is my implementation of thread safe queue 
    //Essentially just a wrapper around Queue<T> with synchronization locks 
    readonly ConcurrentQueue<CheckPrimeTask> mQ; 
    readonly Thread mWorker; 
    bool mStop; 

    public WorkerThread (ConcurrentQueue<CheckPrimeTask> aQ) { 
     mQ = aQ; 
     mWorker = new Thread (Work) {IsBackground = true}; 
     mStop = false; 
    } 

    private void Work() { 
     while (!mStop) { 
      if (mQ.Count == 0) { 
       Thread.Sleep (0); 
       continue; 
      } 

      var task = mQ.Dequeue(); 
      //Someone else might have been lucky in stealing 
      //the task by the time we dequeued it!! 
      if (task == null) 
       continue; 

      task.IsPrime = IsPrime (task.Number); 
      task.ExecutedBy = Thread.CurrentThread.ManagedThreadId; 
      //Ask the threadpool to execute the task callback to 
      //notify completion 
      ThreadPool.QueueUserWorkItem (task.CallBack, task); 
     } 
    } 

    private bool IsPrime (int number) { 
     int limit = Convert.ToInt32 (Math.Sqrt (number)); 
     for (int i = 2; i <= limit; i++) { 
      if (number % i == 0) 
       return false; 
     } 

     return true; 
    } 

    public void Start() { 
     mStop = false; 
     mWorker.Start(); 
    } 

    public void Stop() { 
     mStop = true; 
    } 
} 

Le problème est que lorsque la file d'attente est vide, il consomme trop CPU (près de 98%). J'ai essayé AutoResetEvent d'informer les travailleurs que la file d'attente a été modifiée. Donc, ils attendent effectivement que ce signal se règle. Il a baissé le CPU à près de 0% mais je ne suis pas tout à fait sûr si c'est la meilleure méthode. Pouvez-vous suggérer une meilleure méthode pour garder les threads inactifs sans nuire à l'utilisation du processeur?

+3

à l'aide d'un événement de remise à zéro va être la meilleure approche. –

+2

Une autre possibilité est d'utiliser ThreadPool.QueueUserWorkItem –

+0

Une autre est d'utiliser une minuterie pour vérifier la file d'attente et le feu de threads de travail. –

Répondre

7

Découvrez cette implémentation d'un BlockingQueue. Si la file d'attente est vide, elle utilise Monitor.Wait() pour mettre le thread en veille. Lorsqu'un élément est ajouté, il utilise Monitor.Pulse() pour réveiller un thread qui dort dans la file d'attente vide. Une autre technique consiste à utiliser semaphore. Chaque fois que vous ajoutez un élément à une file d'attente, appelez Release(). Lorsque vous voulez un élément d'une file d'attente, appelez WaitOne().

+0

J'aime l'idée de Semaphore car elle garantit que si un élément est ajouté dans la file d'attente, un seul thread est éveillé. L'utilisation de l'événement reset réveille tous les threads de travail. Merci. – Hemant

+0

Mais il a un downsite, je ** dois ** spécifier les éléments maximum autorisés dans la file d'attente lorsque je crée un sémaphore! Les sémaphores sont-ils suffisamment efficaces pour gérer un très grand nombre? – Hemant

+0

En réponse au premier commentaire. Monitor.Pulse réveille seulement un thread (Monitor.PulseAll) réveillerait chaque thread afin qu'il n'y ait pas de différence. –

0

Vous avez quelques options auxquelles je peux penser. Un moyen consiste à placer un petit fil de sommeil pendant votre boucle. Cela va essentiellement faire tomber votre utilisation du processeur à 0 et est assez moyen de le faire.

Une autre façon est d'utiliser une remise à zéro (soit automatique ou manuel) tel que suggéré par Mitch blé dans les commentaires.

Vous pouvez également concevoir une sorte de IdleTask qui a un thread de sommeil pendant un certain laps de temps et si votre file d'attente est vide, il suffit de traiter la IdleTask (qui va filer sleep).

2

Vous avez actuellement Thread.Sleep(0) dans votre méthode de travail où il n'y a pas d'éléments de file d'attente. Changez-le en quoi que ce soit supérieur à 0 et l'utilisation de votre processeur va diminuer. Essayez 10 commencer par ...

0

Si votre file d'attente est thread-safe, alors vous auriez pas besoin de le faire ...

//Someone else might have been lucky in stealing 
    //the task by the time we dequeued it!! 
    if (task == null) 
     continue; 
Questions connexes