2011-01-23 16 views
5

Ho un ciclo Parallel.ForEach() che prende un elenco di URL e li scarica ciascuno per qualche ulteriore elaborazione. Al di fuori del mio ciclo ho dichiarato una variabile del contatore del ciclo e all'interno del corpo del ciclo io uso Interlocked.Increment() pensando che questo sarebbe il modo migliore per mantenere un modo "thread safe" di aumentare il conteggio quando viene eseguita ciascuna interazione del ciclo.Interlocked.Increment() non funziona come mi aspetto nella Task Parallel Library

int counter = 0; 

Parallel.ForEach(urlList, (url, state) => 
{ 
    // various code statments 

    Interlocked.Increment(ref counter); 

    Debug.WriteLine(" ......... counter: " + counter); 
}); 

avrei pensato che avrei visto qualcosa di simile a:

......... 1 
......... 2 
......... 3 
......... 4 
......... 5 
......... 
......... 
......... n 

Ma quello che ottengo è invece 16 "......... 0" (questo è perché io avere un computer quad-core doppio con 8 core nativi, ma l'hyper-threading è abilitato e mi dà un totale di 16 core). Quindi inizierò a vedere il contatore incrementato normalmente per la maggior parte, ma a volte vedrò dei valori contatore duplicati o anche triplicati nell'output Debug.

Utilizzo di Parallel.ForEach() qual è il modo migliore per contare le iterazioni del ciclo? Grazie per qualsiasi consiglio.

+0

I valori duplicati sono previsti. Ma non capisco da dove vengono gli 0. – CodesInChaos

+0

Un work around sta usando int 'myCounter = Interloced.Increment (contatore ref); Debug.WriteLine (myCounter); ' – CodesInChaos

+3

Puoi pubblicare del codice che in realtà compila ed espone il problema (cioè stampe 0s)? – CodesInChaos

risposta

12

Interlocked.Increment restituirà il valore incrementato.

Quindi,

int counter = 0; 

Parallel.ForEach(urlList, (url, state) => 
{ 
    // various code statments 

    var counterNow = Interloced.Increment(ref counter); 

    Debug.WriteLine(" ......... counter: " + counterNow); 
}); 

dovrebbe restituire il valore del contatore come è stato incrementato.

Edited, parecchio tempo dopo:

A titolo di spiegazione:

Quando si esegue su un multi macchine processore/multi-core e dispone di più thread dell'applicazione, è necessario essere consapevoli che i valori visualizzati per le variabili potrebbero non riflettere lo stato corrente attuale di quella variabile. Questo perché ci possono essere molte cache individuali, per ogni CPU o die o socket e il thread può leggere il valore memorizzato nella cache (salvando un hit per leggere la memoria principale)

Se si desidera solo leggere un valore che è aggiornato da più thread, è necessario utilizzare Interlocked.Read() per garantire di avere il valore corrente per counter.

+0

Questo ha fatto il trucco. Grazie a Mr. Hughes e CodeInChaos sopra per lo stesso suggerimento. – BonanzaDriver

7

Questo perché Increment + WriteLine insieme non sono atomici.
Potrebbe essere che il thread1 incrementi counter, quindi thread2 lo incrementi nuovamente, quindi i due thread raggiungono la parte WriteLine con lo stesso valore di counter.

+1

Ma perché il 'counter' viene mai scritto come' 0'? Il 'WriteLine()' si verificherà solo dopo almeno una chiamata 'Interlocked.Increment()'. –

+0

prova a dichiarare il contatore come volatile. –

+1

L'interblocco implica già una barriera mem così volatile che non dovrebbe fare la differenza. –

1

Non sono sicuro, ma penso che questo sia probabilmente dovuto al modo in cui le variabili acquisiscono il lavoro in lambda. Hai provato a metterlo in una funzione separata o spostare la variabile all'esterno e dichiararla come statica?

Problemi correlati