2009-10-14 13 views
10

Ho un oggetto che richiede molta inizializzazione (1-2 secondi su una macchina robusta). Anche se una volta inizializzato ci vogliono solo circa 20 millisecondi per fare un tipico "lavoro"Modo corretto per avere un thread infinito di lavoro?

Per evitare che venga reinizializzato ogni volta che un'app vuole usarlo (che potrebbe essere 50 volte al secondo o no a tutti per i minuti nell'uso tipico), ho deciso di dargli un job que, e farlo girare sul proprio thread, controllando se ci sono dei lavori per farlo in que. Tuttavia non sono del tutto sicuro di come realizzare un thread che funziona indefinitamente con o senza lavoro.

Ecco quello che ho finora, ogni critica è accolto

private void DoWork() 
    { 
     while (true) 
     { 
      if (JobQue.Count > 0) 
      { 
       // do work on JobQue.Dequeue() 
      } 
      else 
      { 
       System.Threading.Thread.Sleep(50); 
      } 
     } 
    } 

Dopo pensiero: Stavo pensando posso avere bisogno di uccidere questa discussione con grazia Insead di farlo funzionare sempre, quindi penso di aggiungere una Tipo di lavoro che indica la fine del thread. Anche i pensieri su come terminare un thread come questo sono apprezzati.

risposta

19

È necessità-lock in ogni caso, in modo da poter Wait e Pulse:

while(true) { 
    SomeType item; 
    lock(queue) { 
     while(queue.Count == 0) { 
      Monitor.Wait(queue); // releases lock, waits for a Pulse, 
           // and re-acquires the lock 
     } 
     item = queue.Dequeue(); // we have the lock, and there's data 
    } 
    // process item **outside** of the lock 
} 

con aggiungere come:

lock(queue) { 
    queue.Enqueue(item); 
    // if the queue was empty, the worker may be waiting - wake it up 
    if(queue.Count == 1) { Monitor.PulseAll(queue); } 
} 

si potrebbe anche voler guardare this question, che limita la dimensione della coda (blocco se è troppo pieno).

+0

Coda classica, blocco e pulsazione. –

+0

+1 molto bello ... –

+0

Perché scrivere qualcosa che è già stato scritto? Pfx, piccola. – Will

2

È necessaria una primitiva di sincronizzazione, ad esempio WaitHandle (osservare i metodi statici). In questo modo puoi "segnalare" al thread di lavoro che c'è lavoro. Controlla la coda e continua a funzionare fino a quando la coda non è vuota, momento in cui attende che il mutex la segnali nuovamente.

Effettuare una delle voci di lavoro sia un comando quit troppo, in modo da poter segnalare il thread di lavoro quando è il momento di uscire dal filo

1

Nella maggior parte dei casi, ho fatto questo del tutto simile a come si' ve impostato - ma non nella stessa lingua. Ho avuto il vantaggio di lavorare con una struttura dati (in Python) che bloccherà il thread fino a quando un elemento non verrà messo in coda, annullando la necessità della chiamata a riposo.

Se .NET fornisce una classe come questa, vorrei provare a utilizzarla. Un blocco del thread è molto meglio di un thread che gira su sleep calls.

Il lavoro che puoi passare potrebbe essere semplice come un "nulla"; se il codice riceve un null, sa che è ora di uscire dal tempo e andare a casa.

1

Afferra il Parallel Framework. Ha un BlockingCollection<T> che puoi usare come coda di lavoro. Come lo useresti:

  1. Crea il BlockingCollection < T> che manterrà le tue attività/lavori.
  2. creare un po 'le discussioni che hanno un ciclo senza fine (while (true) {// ottiene lavoro dalla coda)
  3. impostare i thread vanno
  4. aggiungere i lavori alla raccolta quando vengono disponibili

I thread verranno bloccati finché un elemento non viene visualizzato nella raccolta. Chiunque lo accenda lo otterrà (dipende dalla CPU). Sto usando questo ora e funziona benissimo.

Ha anche il vantaggio di affidarsi a MS per scrivere quel bit di codice particolarmente sgradevole in cui più thread accedono alla stessa risorsa. E ogni volta che riesci a convincere qualcun altro a scrivere che dovresti farlo. Supponendo, naturalmente, che abbiano più risorse tecniche/di test ed esperienza combinata di te.

+0

Intendi il CTP con la scritta "Questo CTP è solo a scopo di test"? Non sono sicuro che sia un ottimo consiglio ... in 4.0, bene - ma questa è beta! –

1

Se non si ha realmente bisogno di uscire dal thread (e si desidera solo mantenere l'applicazione in esecuzione) è possibile impostare Thread.IsBackground su true e terminerà quando tutti i thread non di sfondo terminano. Will e Marc hanno entrambe buone soluzioni per gestire la coda.

1

Ho implementato una coda di attività in background senza utilizzare alcun tipo di ciclo while, o pulsante, o in attesa, o, in effetti, toccando oggetti Thread affatto. E sembra funzionare. (Con ciò intendo che negli ambienti di produzione sono stati gestiti migliaia di attività al giorno per gli ultimi 18 mesi senza alcun comportamento imprevisto.) È una classe con due proprietà significative, una Queue<Task> e una BackgroundWorker. Ci sono tre metodi significativi, abbreviati qui:

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    if (TaskQueue.Count > 0) 
    { 
     TaskQueue[0].Execute(); 
    } 
} 

private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    Task t = TaskQueue[0]; 

    lock (TaskQueue) 
    { 
     TaskQueue.Remove(t); 
    } 
    if (TaskQueue.Count > 0 && !BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

public void Enqueue(Task t) 
{ 
    lock (TaskQueue) 
    { 
     TaskQueue.Add(t); 
    } 
    if (!BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

Non è che non c'è nessuna attesa e pulsante. Ma tutto ciò avviene all'interno dello BackgroundWorker. Questo si riattiva solo quando un'attività viene rilasciata nella coda, viene eseguita finché la coda non è vuota e quindi torna in stato di stop.

Sono lontano da un esperto di threading. C'è un motivo per scherzare con System.Threading per un problema come questo se si utilizza un BackgroundWorker fare?

+0

In generale, sono d'accordo che la classe BackgroundWorker è molto spesso una scelta più semplice per la gestione di un'attività in background. In questo caso, devo contestare alcune delle tue affermazioni.Stai usando oggetti threading: la parola chiave 'lock' è zucchero sintattico per' System.Threading.Monitor.Enter() 'e' System.Threading.Monitor.Exit() '. Invece di un ciclo while, il gestore di eventi RunWorkerCompleted chiama 'BackgroundWorker.RunWorkerAsync()'. Penso che la logica di un ciclo di attesa/impulso durante il ciclo potrebbe essere più facile da seguire in questo caso. –

+0

Abbastanza giusto. Probabilmente mi sembra più semplice perché non ho esperienza di attesa/impulso. –

Problemi correlati