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?
risposta
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.
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.
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.
Risposta eccellente, grazie. =) –
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:
- SynchronizedCollection Generic Class;
- SynchronizedKeyedCollection Generic Class;
- SynchronizedReadOnlyCollection Generic Class.
Mi scuso, ho appena imparato il loro exitense da solo. =)
non è vero, ce ne sono alcuni in 'System.Collections.Generic' che funzionano qui e precedenti a 4.0 –
@Steve Townsend: dopo la verifica, hai ragione, ho torto. Inoltre, la collezione generica 'IList
Nessun problema. Ti devo un +1 per aver menzionato le 4.0 collezioni. –
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 Ho appena saputo qualcosa di nuovo oggi! Grazie! =) –
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). –
btw - re 'che tipo di problemi?' - Probabilmente otterrai un'eccezione, in un momento casuale in cui più thread sono in conflitto –