2010-05-12 11 views
12

Sto pianificando di creare l'elenco una volta in un costruttore statico e quindi disporre di più istanze di tale classe di leggerlo (ed eseguire enumerazioni attraverso esso) contemporaneamente senza eseguire alcun blocco.Sicurezza filo dell'elenco C# <T> per i lettori

In questo articolo http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx MS descrive il problema della sicurezza dei thread come segue:

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

Un elenco può supportare contemporaneamente più lettori , a condizione che la raccolta non venga modificata. L'enumerazione tramite una raccolta è intrinsecamente non una procedura thread-safe . Nel raro caso in cui un'enumerazione contenda uno o più accessi di scrittura , l'unico modo per garantire la sicurezza del thread consiste nel bloccare la raccolta durante l'intera enumerazione . Per consentire alla collezione di accedere a più thread per la lettura e scrittura , è necessario implementare la propria sincronizzazione.

"L'enumerazione tramite una raccolta non è intrinsecamente una procedura thread-safe". L'affermazione è ciò che mi preoccupa.

Ciò significa che è thread-safe per lo scenario solo lettori, ma finché non si utilizza l'enumerazione?

O è sicuro per il mio scenario?


Grazie per le risposte. Perché ho bisogno di usare AsReadOnly affatto se funzionerà con o senza?

risposta

1

questo significa che è thread-safe per i lettori unico scenario, ma finché come non si utilizza l'enumerazione? O è sicuro per il mio scenario?

Dipende interamente quando si scrive alla raccolta. Ciò non può coincidere con la lettura (enumerazione) senza alcun tipo di schema di blocco.

Quindi, se lo si riempie una volta e poi si itera semplicemente su di esso, si è al sicuro. Ma quando un thread cambia la lista (aggiungi o rimuovi oggetti) ti servirà per esempio un ReaderWriterLockSlim.

Quando si modifica lo stato di un oggetto memorizzato, la sicurezza del thread è con quell'elemento (non l'elenco).

8

Significa che se si enumera una raccolta mentre un thread diverso (o una propria discussione) la modifica, si avranno problemi.

Finché non si cambia affatto la raccolta e finché non si condivide IEnumerator s attraverso i thread, non si dovrebbero avere problemi.

+0

Per "non condividere IEnumeratori tra thread", si intende "non accedere ** alla stessa istanza di enumeratore ** da più thread"? –

+1

@EugeneBeresovksy: Esattamente. – SLaks

3

Sì, l'elenco è sicuro per uno scenario lettori, se l'elenco non verrà mai modificato, allora sarà ok.

Se è infatti il ​​caso che dopo la costruzione l'elenco non venga modificato, è necessario utilizzare un'interfaccia più appropriata come ReadOnlyCollection. se è memorizzato in una variabile statica pubblica come dici tu dovresti usare quell'interfaccia.

private static List<T> shared_list; 

private static ReadOnlyCollection<T> _data; 
public static IEnumerable<T> Data 
{ 
    get 
    { 
     return _data ?? (_data = shared_list.AsReadOnly()); 
    } 
} 

==== ==== EDIT

Questa versione memorizza nella cache il riferimento per i futuri ReadOnlyCollection più veloci i tempi di ricerca.

Nota, esiste la possibilità che si verifichi una corsa di dati sulla variabile _data se due thread tentano di catturare un riferimento contemporaneamente quando è nullo, ma poiché tutto è letto solo questo non ha molta importanza, solo creare un oggetto ReadOnlyCollection aggiuntivo, che è economico rispetto alla sincronizzazione.

+0

Si noti che non è ancora necessario modificare l'elenco originale. Inoltre, non chiamare "AsReadOnly" ogni volta che si ottiene la proprietà. – SLaks

+0

AsReadOnly è un'operazione O (1) in base alla documentazione MSDN. Credo che si tratti semplicemente di un ReadOnlyCollection che semplicemente delega alla raccolta sottostante. Quindi AsReadOnly dovrebbe essere abbastanza veloce, ma causa un'allocazione quindi potremmo potenzialmente condividere quella memoria. male modificare per riflettere. – luke

+0

Sotto .NET 4 è possibile utilizzare la classe Lazy generica per l'inizializzazione. Vedi http://msdn.microsoft.com/en-us/library/dd642329.aspx – TrueWill

Problemi correlati