2012-05-07 6 views
31

La documentazione di ConcurrentDictionary fa stato non esplicito, quindi credo che non possiamo aspettarci che i delegati valueFactory e updateValueFactory hanno la loro esecuzione sincronizzata (da GetOrAdd() e AddOrUpdate() le operazioni rispettivamente).Pitfall ConcurrentDictionary - Le fabbriche dei delegati da GetOrAdd e AddOrUpdate sono sincronizzate?

Quindi, penso che non possiamo implementare l'uso delle risorse al loro interno che necessitano di controllo parallelo senza implementare manualmente il nostro controllo concomitante, forse solo utilizzando [MethodImpl(MethodImplOptions.Synchronized)] sopra i delegati.

Ho ragione? O il fatto che ConcurrentDictionary sia thread-safe possiamo aspettarci che le chiamate a questi delegati siano automaticamente sincronizzate (thread-safe)?

risposta

33

Sì, hai ragione, i delegati utente non sono sincronizzati da ConcurrentDictionary. Se hai bisogno di quelli sincronizzati è una tua responsabilità.

MSDN stesso dice:

Inoltre, anche se tutti i metodi di ConcurrentDictionary sono thread-safe, non tutti i metodi sono atomiche, specificamente GetOrAdd e AddOrUpdate. L'utente delegato passato a questi metodi è invocato al di fuori del blocco interno del dizionario. (Questo viene fatto per impedire codice sconosciuto da bloccare tutte le discussioni.)

See "How to: Add and Remove Items from a ConcurrentDictionary

Questo perché il ConcurrentDictionary ha idea di cosa il delegato che fornisci farà o le prestazioni, quindi se è tentato di blocco intorno a loro, potrebbe davvero influire negativamente sulle prestazioni e rovinare il valore di ConcurrentDictionary.

Pertanto, è responsabilità dell'utente sincronizzare il proprio delegato se necessario. Il link MSDN sopra in realtà ha un buon esempio delle garanzie che fa e non produce.

+1

Così sto avvolgendo la chiamata del metodo GetOrAdd in un blocco ma questo rende inutile lo scopo del ConcurrentDictionary. C'è un modo migliore? – John

+0

Sono un po 'confuso da questo, stiamo sostanzialmente dicendo che se un delegato viene passato come chiave, che la sua esecuzione non sarà sincronizzata nella valutazione del * valore *? Oppure se abbiamo un dizionario con tipo "delegato" come valore ... che quando proviamo ad eseguire il delegato (cioè il dizionario [chiave] (argomento); ') che l'esecuzione non sarà sincronizzata? O, mi manca completamente il punto? Grazie. – Snoopy

+0

@Snoopy: il delegato che valuta il valore da aggiungere non è sincronizzato. Ciò significa che se il tuo delegato fa qualcosa che non è thread-safe * e * non hai fatto un tentativo di sincronizzare quell'operazione da solo, allora accadranno cose brutte. Ma, la cosa che un delegato che funge da fabbrica per un valore normalmente non sta facendo cose che non sono thread-safe per natura. Il più delle volte il delegato sta probabilmente facendo 'new SomeObject()' che è sicuramente thread-safe perché è un'operazione senza stato. –

22

Non solo questi delegati non sono sincronizzati, ma non è nemmeno garantito che si verifichino una sola volta. Possono, infatti, essere eseguiti più volte per chiamata a AddOrUpdate.

Ad esempio, l'algoritmo per AddOrUpdate è simile al seguente.

TValue value; 
do 
{ 
    if (!TryGetValue(...)) 
    { 
    value = addValueFactory(key); 
    if (!TryAddInternal(...)) 
    { 
     continue; 
    } 
    return value; 
    } 
    value = updateValueFactory(key); 
} 
while (!TryUpdate(...)) 
return value; 

Nota due cose qui.

  • Non c'è alcuno sforzo per sincronizzare l'esecuzione dei delegati.
  • I delegati possono essere eseguiti più di una volta poiché sono stati richiamati all'interno del ciclo.

Quindi è necessario assicurarsi di fare due cose.

  • Fornire la propria sincronizzazione per i delegati.
  • Assicurati che i tuoi delegati non abbiano effetti collaterali che dipendono dal numero di volte in cui sono stati eseguiti.
+4

@Luciano: ho appena controllato e 'GetOrAdd' sembra chiamare' ValueFactory' una sola volta. Quindi è solo "AddOrUpdate" che ha il potenziale per chiamare i delegati più volte. Quello potrebbe essere un bug da parte di Microsoft ... non è sicuro. Ho scoperto questo a lungo mentre [rispondendo a una domanda simile] (http://stackoverflow.com/a/3831892/158779). –

+0

@BrianGideon: Fuori rotta, mi dispiace non ho notato che stavi parlando di AddOrUpdate piuttosto che di GetOrAdd. Grazie per aver risposto. – Luciano

+0

Questo comportamento è stato modificato in qualsiasi modo fino ad ora? – Sebastian

Problemi correlati