2010-01-14 11 views
5

Nella mia attuale applicazione C#/NET 3.5, ho una coda di task (thread safe) e ho 5 thread di lavoro che devono cercare costantemente le attività in coda. Se un'attività è disponibile, qualsiasi lavoratore annulla l'operazione e intraprende l'azione richiesta.Qual è il metodo più efficiente della CPU per rendere i thread di lavoro in attesa di attività?

mio lavoratore classe thread è il seguente:

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

problema è che se la coda è vuota, consuma troppa CPU (quasi il 98%). Ho provato AutoResetEvent per notificare ai lavoratori che la coda è stata cambiata. Quindi aspettano effettivamente che quel segnale si imposti. Ha ridotto la CPU a quasi lo 0%, ma non sono del tutto sicuro che questo sia il metodo migliore. Puoi suggerire un metodo migliore per mantenere i thread inattivi senza danneggiare l'utilizzo della CPU?

+3

utilizzando un evento di reset sta per essere l'approccio migliore. –

+2

Un'altra possibilità è utilizzare ThreadPool.QueueUserWorkItem –

+0

Un'altra ancora è utilizzare un timer per controllare la coda e attivare i thread di lavoro. –

risposta

7

Dai un'occhiata a questa implementazione di BlockingQueue. Se la coda è vuota, utilizza Monitor.Wait() per mettere il thread in stop. Quando viene aggiunto un elemento, utilizza Monitor.Pulse() per riattivare un thread che sta dormendo sulla coda vuota.

Un'altra tecnica consiste nell'utilizzare un semaphore. Ogni volta che aggiungi un articolo a una coda, chiama Release(). Quando si desidera un articolo da una coda, chiamare WaitOne().

+0

Mi piace l'idea di Semaforo poiché assicura che se un elemento viene aggiunto in coda, viene risvegliato solo un thread. L'utilizzo dell'evento di reset sveglia tutti i thread di lavoro. Grazie. – Hemant

+0

Ma ha un downsite, io ** devo ** specificare gli elementi massimi consentiti nella coda quando creo il semaforo! I semafori sono abbastanza efficienti da gestire un conteggio molto grande? – Hemant

+0

In risposta al primo commento. Monitor.Pulse risveglia solo un thread (Monitor.PulseAll) per il risveglio di ogni thread, quindi non c'è differenza. –

0

Avete un paio di opzioni a cui posso pensare.

Un modo è di posizionare un thread piccolo durante il ciclo. In pratica questo diminuirà l'utilizzo della CPU a 0 ed è un modo abbastanza standard per farlo.

Un altro modo è utilizzare un ripristino (automatico o manuale) come suggerito da Mitch Wheat nei commenti.

Si potrebbe anche escogitare una sorta di IdleTask con un thread di sospensione per un determinato periodo di tempo e se la propria coda è vuota, elaborare solo IdleTask (che eseguirà il thread di sospensione).

2

Attualmente hai Thread.Sleep(0) nel tuo metodo di lavoro per cui non ci sono elementi in coda. Passa a qualsiasi valore maggiore di 0 e l'utilizzo della CPU diminuirà. Prova 10 per cominciare ...

0

Se la coda è sicuro quindi non avrebbe bisogno di fare questa discussione ...

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