2010-10-28 1 views
15

Posso, senza bloccare, chiamare in modo sicuro List.AddRange (r) da più thread? In caso contrario, che tipo di problemi dovrei incontrare?Il thread <T> .AddRange() è sicuro?

+1

btw - re 'che tipo di problemi?' - Probabilmente otterrai un'eccezione, in un momento casuale in cui più thread sono in conflitto –

risposta

17

No, its documentation non dice che è thread-safe, quindi non lo è.

Public static (Shared in Visual Basic) membri di questo tipo sono thread-safe. Qualsiasi membro di istanza non è garantito come thread-safe.

Per quanto riguarda ciò che può andare storto, pensare a ciò che AddRange (newItems) fa:

  • Verificare se c'è abbastanza spazio nella matrice interna
  • In caso contrario:
    • Allocare un nuovo array
    • Copia gli elementi correnti nel nuovo array
    • Imposta un campo per puntare sul nuovo arr ay
  • Copiare i newItems al locale corretta nella matrice interna
  • Aggiornare il campo “contare” (questo è usato per controllare dove è inserita la voce successiva)

Ora pensare cosa capita se quanto sopra è mescolato con un'altra chiamata a AddRange() o anche solo una chiamata per leggere un elemento.

1

No, non è thread-safe.

Il thread A può chiamare AddRange nell'elenco. Potrebbe scorrere parzialmente la raccolta e passare i thread.

Il thread B può chiamare Aggiungi/Rimuovi, ecc. Prima che il thread A abbia finito.

6

No, non lo è, ma mi piacerebbe aggiungere che è più efficiente fare un myList.AddRange(...); all'interno di un blocco piuttosto che fare diversi lock (syncLock) { myList.Add(...) };.

In che tipo di problemi potresti incontrare? Quando un thread aggiunge un elemento mentre un altro enumera l'elenco, List<T> genererà una certa eccezione perché esegue un controllo delle versioni interno, poiché vuole impedire a noi sviluppatori poveri di ottenere effetti collaterali indesiderati.

Anche lo List<T> conserva internamente un array in cui memorizza i suoi articoli. Forse impostare un elemento in un array è piuttosto atomico, ma ogni volta che viene raggiunta la capacità di questo array, ne verrà creato uno nuovo e gli elementi verranno copiati da quello precedente. Quindi, quando un thread vuole aggiungere qualcosa mentre quella copia avviene, puoi immaginare che le cose non vadano fuori sincrono.

+0

Risposta eccellente, grazie. =) –

3

Fino a .NET Framework 4.0, nessuna raccolta .NET è thread-safe. Ti verrà richiesto di bloccarlo prima di accedervi nel tuo codice Collections and Synchronization (Thread Safety).

D'altra parte, .NET Framework 4.0 introduce il nuovo spazio dei nomi System.Collections.Concurrent che include Thread-Safe Collections a grana fine.

Infine, se è possibile utilizzare .NET Framework 4.0, si consiglia vivamente di farlo per ciò che è necessario, in caso contrario, assicurarsi di bloccare la raccolta ogni volta che si desidera modificare o accedervi.

Inoltre, una raccolta statica dovrebbe essere thread-safe, ma attenzione, in quanto i membri non sono garantiti.

EDIT # 1

dopo ulteriori verifiche a causa di un commento di Steve Townsend, ammetto che ci sono tre collezioni thread-safe all'interno del .NET Framework a partire dalla versione 3.0:

  1. SynchronizedCollection Generic Class;
  2. SynchronizedKeyedCollection Generic Class;
  3. SynchronizedReadOnlyCollection Generic Class.

Mi scuso, ho appena imparato il loro exitense da solo. =)

+0

non è vero, ce ne sono alcuni in 'System.Collections.Generic' che funzionano qui e precedenti a 4.0 –

+0

@Steve Townsend: dopo la verifica, hai ragione, ho torto. Inoltre, la collezione generica 'IList ' non lo è, anche se non avevo considerato questo 'SynchronizedCollection ', dal momento che non ne ero a conoscenza. = P Grazie per avermi informato! –

+1

Nessun problema. Ti devo un +1 per aver menzionato le 4.0 collezioni. –

3

A seconda dell'utilizzo, SynchronizedCollection potrebbe funzionare.

Non avresti un singolo scatto AddRange però. Se si utilizza questo solo per inizializzare la raccolta, è possibile farlo in quanto esiste un sovraccarico del costruttore IEnumerable.

+1

+1 Ho appena saputo qualcosa di nuovo oggi! Grazie! =) –

+0

Grazie per il tuo upvote. Vorrei farti sapere che ho modificato la mia risposta per riflettere le nuove informazioni che mi hai fornito (tutti noi). –