2012-07-15 10 views
5
  • Ho un'operazione intensiva di I/O.
  • Voglio solo un MAX di 5 thread in esecuzione in una volta.
  • Ho 8000 compiti da completare e accodare.
  • Ogni operazione richiede circa 15-20 secondi per l'esecuzione.

Ho guardato in giro a ThreadPool, maFrustrazione di ThreadPool - Creazione di thread superiore a SetMaxThreads

 ThreadPool.SetMaxThreads(5, 0); 

     List<task> tasks = GetTasks(); 

     int toProcess = tasks.Count; 
     ManualResetEvent resetEvent = new ManualResetEvent(false); 

     for (int i = 0; i < tasks.Count; i++) 
     { 
      ReportGenerator worker = new ReportGenerator(tasks[i].Code, id); 
      ThreadPool.QueueUserWorkItem(x => 
      { 
       worker.Go(); 
       if (Interlocked.Decrement(ref toProcess) == 0) 
        resetEvent.Set(); 
      }); 
     } 

     resetEvent.WaitOne(); 

non riesco a capire perché ... il mio codice è in esecuzione più di 5 filetti in una sola volta. Ho provato a setmaxthreads, setminthreads, ma continua a eseguire più di 5 thread.

Cosa sta succedendo? Cosa mi manca? Dovrei farlo in un altro modo?

Grazie

+0

Hai verificato il valore di ** tasks.Count ** nel debugger? Hai provato a inserire semplicemente "5"? –

+0

L'array di compiti ha ~ 8000 oggetti in esso – Mike

risposta

3

Task Parallel Library può aiutare:

List<task> tasks = GetTasks(); 

Parallel.ForEach(tasks, new ParallelOptions { MaxDegreeOfParallelism = 5 }, 
    task => {ReportGenerator worker = new ReportGenerator(task.Code, id); 
      worker.Go();}); 

What does MaxDegreeOfParallelism do?

+0

Questo è così semplice! E ha funzionato come un fascino! Grazie! – Mike

1

Penso che ci sia un modo diverso e migliore per avvicinarsi a questo. (Perdonami se accidentalmente Java-ize alcuni della sintassi)

Il thread principale qui ha un elenco di cose da fare in "Attività" - invece di creare thread per ogni attività, che non è davvero efficiente quando si avere così tanti articoli, creare il numero desiderato di thread e poi chiedere loro di richiedere le attività dall'elenco secondo necessità.

La prima cosa da fare è aggiungere una variabile alla classe da cui proviene questo codice, da usare come puntatore nella lista. Aggiungeremo anche uno per il numero massimo di thread desiderato.

// New variable in your class definition 
private int taskStackPointer; 
private final static int MAX_THREADS = 5; 

Creare un metodo che restituisce l'attività successiva nell'elenco e incrementa il puntatore dello stack. Quindi creare una nuova interfaccia per questo:

// Make sure that only one thread has access at a time 
[MethodImpl(MethodImplOptions.Synchronized)] 
public task getNextTask() 
{ 
    if(taskStackPointer < tasks.Count) 
     return tasks[taskStackPointer++]; 
    else 
     return null; 
} 

In alternativa, si potrebbe tornare compiti [taskStackPointer ++] il codice, se c'è un valore che si può designare come che significa "fine della lista".. Probabilmente è più facile farlo in questo modo, comunque.

L'interfaccia:

public interface TaskDispatcher 
{ 
    [MethodImpl(MethodImplOptions.Synchronized)] public task getNextTask(); 
} 

All'interno della classe ReportGenerator, modificare il costruttore ad accettare l'oggetto dispatcher:

public ReportGenerator(TaskDispatcher td, int idCode) 
{ 
    ... 
} 

Avrete anche bisogno di modificare il ReportGenerator classe in modo che il l'elaborazione ha un ciclo esterno che inizia chiamando td.getNextTask() per richiedere una nuova attività e quale esce dal ciclo quando viene restituito un valore NULL.

Infine, modificare il codice di creazione thread per qualcosa di simile: (questo è solo per dare un'idea)

taskStackPointer = 0; 
for (int i = 0; i < MAX_THREADS; i++) 
{ 
    ReportGenerator worker = new ReportGenerator(this,id); 
    worker.Go(); 
} 

In questo modo si crea il numero desiderato di fili e tenerli tutti a lavorare a capacità massima .

(Non sono sicuro di aver utilizzato "[MethodImpl (MethodImplOptions.Synchronized)]" esattamente corretto ...Sono più abituati a Java che C#)

+0

Grazie per aver trovato il tempo di rispondere alla mia domanda, ha senso. Tuttavia, questo metodo è più dettagliato: P – Mike

+0

Potrebbe essere un po 'più prolisso ma una volta installato è abbastanza efficiente e facile da capire. –

1

Il tuo elenco di attività avrà 8k elementi in essa, perché hai detto il codice per metterli lì :

List<task> tasks = GetTasks(); 

detto questo, questo numero non ha nulla a che fare con il numero di thread vengono utilizzati, nel senso che il debugger è sempre andando a mostrare come molti articoli che annuncio ded alla lista.

Esistono vari modi per determinare quanti thread sono in uso. Forse uno dei più semplici è quello di entrare nell'applicazione con il debugger e dare un'occhiata alla finestra dei thread. Non solo otterrai un conteggio, ma vedrai cosa sta facendo (o meno) ogni thread che mi porta a ...

C'è una discussione significativa su cosa stanno facendo i tuoi compiti e come sei arrivato a un numero per "accelerare" il pool di thread. Nella maggior parte dei casi d'uso, il pool di thread farà la cosa giusta.

Ora per rispondere alla tua domanda specifica ...

Per controllare in modo esplicito il numero di attività simultanee, si consideri un'implementazione banale che avrebbe coinvolto cambiare la vostra collezione di un'attività da un elenco a BlockingCollection (che utilizzerà al suo interno un ConcurrentQueue) e il seguente codice per 'consumare' il lavoro:

var parallelOptions = new ParallelOptions 
{ 
    MaxDegreeOfParallelism = 5 
}; 

Parallel.ForEach(collection.GetConsumingEnumerable(), options, x => 
{ 
    // Do work here... 
}); 

Change MaxDegreeOfParallelism a qualsiasi valore concomitante aver individuato è appropriato per il lavoro che state facendo.

Il seguente potrebbe essere di vostro interesse:

Parallel.ForEach Method

BlockingCollection

Chris

3

C'è una limitazione in SetMaxThreads a che non si può mai impostare inferiore al numero di processori sul sistema. Se hai 8 processori, impostarlo su 5 equivale a non chiamare affatto la funzione.