Propriedade da lista de segurança do tópico

quero uma implementação de List<T> como uma propriedade que pode ser usada thread-com segurança, sem qualquer dúvida.

Algo do género:
private List<T> _list;

private List<T> MyT
{
    get { // return a copy of _list; }
    set { _list = value; }
}

parece que ainda preciso devolver uma cópia (clonada) da colecção, por isso, se algures estamos a iterar a colecção e, ao mesmo tempo, a colecção está configurada, então nenhuma excepção é levantada.

Como implementar uma propriedade de recolha segura de fios?

Author: dsolimano, 2011-05-03

12 answers

Se está a ser alvo do. Net 4, existem algumas opções no sistema .Coleccao.Simultaneous Namespace

Neste caso pode usar ConcurrentBag<T> em vez de List<T>

 141
Author: Bala R, 2011-05-03 19:09:53
Mesmo tendo a maioria dos votos, normalmente não se pode tomar como um substituto de Road-safe para System.Collections.Generic.List<T> como ele é (Radek Stromský já apontou isso) não ordenado.

Mas há uma classe chamada System.Collections.Generic.SynchronizedCollection<T> que já está desde então .NET 3.0 parte do framework, mas é que bem escondido em um local onde não se espera é que ele é pouco conhecido e, provavelmente, você nunca tropeçou sobre ele (pelo menos eu nunca fiz).

SynchronizedCollection<T> é compilado em montagem sistema.ServiceModel.dll (que faz parte do perfil do cliente mas não da biblioteca de classes portátil).

Espero que isso ajude.
 68
Author: Christoph, 2013-10-17 09:59:03
Acho que fazer uma aula de Triadsafelista seria fácil.
public class ThreadSafeList<T> : IList<T>
{
    protected List<T> _interalList = new List<T>();

    // Other Elements of IList implementation

    public IEnumerator<T> GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    protected static object _lock = new object();

    public List<T> Clone()
    {
        List<T> newList = new List<T>();

        lock (_lock)
        {
            _interalList.ForEach(x => newList.Add(x));
        }

        return newList;
    }
}

Você simplesmente clona a lista antes de solicitar um enumerador, e assim qualquer enumeração está trabalhando fora de uma cópia que não pode ser modificada durante a execução.

 15
Author: Tejs, 2011-05-03 19:15:35

Mesmo a resposta Aceita é simultaneamente, eu não acho que seja a substituição real da lista em todos os casos, como o comentário de Radek para a resposta diz: "a coleção ConcurrentBag não é ordenada, então ao contrário da lista não garante ordenação. Também você não pode acessar itens por índice".

Por isso, se usar o.net 4. 0 ou mais, uma solução de trabalho poderá ser usar ConcurrentDictionary com o inteiro TKey como índice de matriz e o valor do valor do valor do valor do valor do valor do valor da matriz. Esta é a forma recomendada de substituir a lista em Pluralsight C# curso de colecções concorrentes . O ConcurrentDictionary resolve ambos os problemas mencionados acima: o acesso ao índice e a ordenação (não podemos confiar na ordenação, uma vez que é a tabela de hash sob o capô, mas a implementação atual.net salva a ordem de adição de elementos).

 4
Author: tytyryty, 2017-03-31 18:49:04

Pode usar:

var threadSafeArrayList = ArrayList.Synchronized(new ArrayList());

Para Criar Uma Matrizes De Segurança Do Tópico

 2
Author: Hani Nakhli, 2017-10-08 18:24:59

Se você olhar o código-fonte para a Lista de T (https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877) você vai notar que há uma classe de lá (que é, naturalmente, interna - por isso, a Microsoft, por quê?!?!) called SynchronizedList of T. I am copy pasting the code here:

   [Serializable()]
    internal class SynchronizedList : IList<T> {
        private List<T> _list;
        private Object _root;

        internal SynchronizedList(List<T> list) {
            _list = list;
            _root = ((System.Collections.ICollection)list).SyncRoot;
        }

        public int Count {
            get {
                lock (_root) { 
                    return _list.Count; 
                }
            }
        }

        public bool IsReadOnly {
            get {
                return ((ICollection<T>)_list).IsReadOnly;
            }
        }

        public void Add(T item) {
            lock (_root) { 
                _list.Add(item); 
            }
        }

        public void Clear() {
            lock (_root) { 
                _list.Clear(); 
            }
        }

        public bool Contains(T item) {
            lock (_root) { 
                return _list.Contains(item);
            }
        }

        public void CopyTo(T[] array, int arrayIndex) {
            lock (_root) { 
                _list.CopyTo(array, arrayIndex);
            }
        }

        public bool Remove(T item) {
            lock (_root) { 
                return _list.Remove(item);
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            lock (_root) { 
                return _list.GetEnumerator();
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            lock (_root) { 
                return ((IEnumerable<T>)_list).GetEnumerator();
            }
        }

        public T this[int index] {
            get {
                lock(_root) {
                    return _list[index];
                }
            }
            set {
                lock(_root) {
                    _list[index] = value;
                }
            }
        }

        public int IndexOf(T item) {
            lock (_root) {
                return _list.IndexOf(item);
            }
        }

        public void Insert(int index, T item) {
            lock (_root) {
                _list.Insert(index, item);
            }
        }

        public void RemoveAt(int index) {
            lock (_root) {
                _list.RemoveAt(index);
            }
        }
    }
Pessoalmente, eu acho que eles sabiam que uma melhor implementação usando [[7]] Semaforese poderia ser criada, mas não chegou a ela.
 2
Author: Siderite Zackwehdex, 2018-08-12 19:46:12

Você também pode usar o mais primitivo

Monitor.Enter(lock);
Monitor.Exit(lock);

Que bloqueio usa(veja esta publicação C# bloqueando um objecto que é realocado no bloco de bloqueio ).

Se está à espera de excepções no código, isto não é seguro, mas permite - lhe fazer algo do género:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;

public class Something
{
    private readonly object _lock;
    private readonly List<string> _contents;

    public Something()
    {
        _lock = new object();

        _contents = new List<string>();
    }

    public Modifier StartModifying()
    {
        return new Modifier(this);
    }

    public class Modifier : IDisposable
    {
        private readonly Something _thing;

        public Modifier(Something thing)
        {
            _thing = thing;

            Monitor.Enter(Lock);
        }

        public void OneOfLotsOfDifferentOperations(string input)
        {
            DoSomethingWith(input);
        }

        private void DoSomethingWith(string input)
        {
            Contents.Add(input);
        }

        private List<string> Contents
        {
            get { return _thing._contents; }
        }

        private object Lock
        {
            get { return _thing._lock; }
        }

        public void Dispose()
        {
            Monitor.Exit(Lock);
        }
    }
}

public class Caller
{
    public void Use(Something thing)
    {
        using (var modifier = thing.StartModifying())
        {
            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("B");

            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("A");
        }
    }
}

Uma das coisas boas sobre isso é que você vai ter o bloqueio para a duração da série de operações (em vez de bloquear em cada operação). O que significa que a saída deve sair em os blocos da direita (a minha Utilização disto era obter alguma saída para o ecrã a partir de um processo externo)

Eu realmente gosto da simplicidade + transparência da ThreadSafeList + que faz a parte importante em parar acidentes

 1
Author: Jonny Leeds, 2017-05-23 11:47:29

Acredito que _list.ToList() te fará uma cópia. Você também pode questioná-lo se você precisar de tais como :

_list.Select("query here").ToList(); 

De qualquer forma, msdn diz que isto é realmente uma cópia e não simplesmente uma referência. Oh, e sim, você vai precisar de bloquear no método set como os outros têm apontado.

 0
Author: Jonathan Henson, 2016-08-18 06:01:16
Parece que muitas das pessoas que encontram isto querem uma colecção de segurança de fios indexada dinamicamente. A coisa mais próxima e mais fácil que conheço seria.
System.Collections.Concurrent.ConcurrentDictionary<int, YourDataType>
Isto exigiria que garantisse que a sua chave está incriminada, se quiser um comportamento normal de indexação. Se tiveres cuidado .a contagem pode ser suficiente como chave para quaisquer novos pares de valores que você adicione.
 0
Author: user2163234, 2018-10-01 16:26:58
Aqui está a aula que pediu:
namespace AI.Collections {
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;

    /// <summary>
    ///     Just a simple thread safe collection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <value>Version 1.5</value>
    /// <remarks>TODO replace locks with AsyncLocks</remarks>
    [DataContract( IsReference = true )]
    public class ThreadSafeList<T> : IList<T> {
        /// <summary>
        ///     TODO replace the locks with a ReaderWriterLockSlim
        /// </summary>
        [DataMember]
        private readonly List<T> _items = new List<T>();

        public ThreadSafeList( IEnumerable<T> items = null ) { this.Add( items ); }

        public long LongCount {
            get {
                lock ( this._items ) {
                    return this._items.LongCount();
                }
            }
        }

        public IEnumerator<T> GetEnumerator() { return this.Clone().GetEnumerator(); }

        IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }

        public void Add( T item ) {
            if ( Equals( default( T ), item ) ) {
                return;
            }
            lock ( this._items ) {
                this._items.Add( item );
            }
        }

        public Boolean TryAdd( T item ) {
            try {
                if ( Equals( default( T ), item ) ) {
                    return false;
                }
                lock ( this._items ) {
                    this._items.Add( item );
                    return true;
                }
            }
            catch ( NullReferenceException ) { }
            catch ( ObjectDisposedException ) { }
            catch ( ArgumentNullException ) { }
            catch ( ArgumentOutOfRangeException ) { }
            catch ( ArgumentException ) { }
            return false;
        }

        public void Clear() {
            lock ( this._items ) {
                this._items.Clear();
            }
        }

        public bool Contains( T item ) {
            lock ( this._items ) {
                return this._items.Contains( item );
            }
        }

        public void CopyTo( T[] array, int arrayIndex ) {
            lock ( this._items ) {
                this._items.CopyTo( array, arrayIndex );
            }
        }

        public bool Remove( T item ) {
            lock ( this._items ) {
                return this._items.Remove( item );
            }
        }

        public int Count {
            get {
                lock ( this._items ) {
                    return this._items.Count;
                }
            }
        }

        public bool IsReadOnly { get { return false; } }

        public int IndexOf( T item ) {
            lock ( this._items ) {
                return this._items.IndexOf( item );
            }
        }

        public void Insert( int index, T item ) {
            lock ( this._items ) {
                this._items.Insert( index, item );
            }
        }

        public void RemoveAt( int index ) {
            lock ( this._items ) {
                this._items.RemoveAt( index );
            }
        }

        public T this[ int index ] {
            get {
                lock ( this._items ) {
                    return this._items[ index ];
                }
            }
            set {
                lock ( this._items ) {
                    this._items[ index ] = value;
                }
            }
        }

        /// <summary>
        ///     Add in an enumerable of items.
        /// </summary>
        /// <param name="collection"></param>
        /// <param name="asParallel"></param>
        public void Add( IEnumerable<T> collection, Boolean asParallel = true ) {
            if ( collection == null ) {
                return;
            }
            lock ( this._items ) {
                this._items.AddRange( asParallel
                                              ? collection.AsParallel().Where( arg => !Equals( default( T ), arg ) )
                                              : collection.Where( arg => !Equals( default( T ), arg ) ) );
            }
        }

        public Task AddAsync( T item ) {
            return Task.Factory.StartNew( () => { this.TryAdd( item ); } );
        }

        /// <summary>
        ///     Add in an enumerable of items.
        /// </summary>
        /// <param name="collection"></param>
        public Task AddAsync( IEnumerable<T> collection ) {
            if ( collection == null ) {
                throw new ArgumentNullException( "collection" );
            }

            var produce = new TransformBlock<T, T>( item => item, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount } );

            var consume = new ActionBlock<T>( action: async obj => await this.AddAsync( obj ), dataflowBlockOptions: new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount } );
            produce.LinkTo( consume );

            return Task.Factory.StartNew( async () => {
                collection.AsParallel().ForAll( item => produce.SendAsync( item ) );
                produce.Complete();
                await consume.Completion;
            } );
        }

        /// <summary>
        ///     Returns a new copy of all items in the <see cref="List{T}" />.
        /// </summary>
        /// <returns></returns>
        public List<T> Clone( Boolean asParallel = true ) {
            lock ( this._items ) {
                return asParallel
                               ? new List<T>( this._items.AsParallel() )
                               : new List<T>( this._items );
            }
        }

        /// <summary>
        ///     Perform the <paramref name="action" /> on each item in the list.
        /// </summary>
        /// <param name="action">
        ///     <paramref name="action" /> to perform on each item.
        /// </param>
        /// <param name="performActionOnClones">
        ///     If true, the <paramref name="action" /> will be performed on a <see cref="Clone" /> of the items.
        /// </param>
        /// <param name="asParallel">
        ///     Use the <see cref="ParallelQuery{TSource}" /> method.
        /// </param>
        /// <param name="inParallel">
        ///     Use the
        ///     <see
        ///         cref="Parallel.ForEach{TSource}(System.Collections.Generic.IEnumerable{TSource},System.Action{TSource})" />
        ///     method.
        /// </param>
        public void ForEach( Action<T> action, Boolean performActionOnClones = true, Boolean asParallel = true, Boolean inParallel = false ) {
            if ( action == null ) {
                throw new ArgumentNullException( "action" );
            }
            var wrapper = new Action<T>( obj => {
                try {
                    action( obj );
                }
                catch ( ArgumentNullException ) {
                    //if a null gets into the list then swallow an ArgumentNullException so we can continue adding
                }
            } );
            if ( performActionOnClones ) {
                var clones = this.Clone( asParallel: asParallel );
                if ( asParallel ) {
                    clones.AsParallel().ForAll( wrapper );
                }
                else if ( inParallel ) {
                    Parallel.ForEach( clones, wrapper );
                }
                else {
                    clones.ForEach( wrapper );
                }
            }
            else {
                lock ( this._items ) {
                    if ( asParallel ) {
                        this._items.AsParallel().ForAll( wrapper );
                    }
                    else if ( inParallel ) {
                        Parallel.ForEach( this._items, wrapper );
                    }
                    else {
                        this._items.ForEach( wrapper );
                    }
                }
            }
        }

        /// <summary>
        ///     Perform the <paramref name="action" /> on each item in the list.
        /// </summary>
        /// <param name="action">
        ///     <paramref name="action" /> to perform on each item.
        /// </param>
        /// <param name="performActionOnClones">
        ///     If true, the <paramref name="action" /> will be performed on a <see cref="Clone" /> of the items.
        /// </param>
        /// <param name="asParallel">
        ///     Use the <see cref="ParallelQuery{TSource}" /> method.
        /// </param>
        /// <param name="inParallel">
        ///     Use the
        ///     <see
        ///         cref="Parallel.ForEach{TSource}(System.Collections.Generic.IEnumerable{TSource},System.Action{TSource})" />
        ///     method.
        /// </param>
        public void ForAll( Action<T> action, Boolean performActionOnClones = true, Boolean asParallel = true, Boolean inParallel = false ) {
            if ( action == null ) {
                throw new ArgumentNullException( "action" );
            }
            var wrapper = new Action<T>( obj => {
                try {
                    action( obj );
                }
                catch ( ArgumentNullException ) {
                    //if a null gets into the list then swallow an ArgumentNullException so we can continue adding
                }
            } );
            if ( performActionOnClones ) {
                var clones = this.Clone( asParallel: asParallel );
                if ( asParallel ) {
                    clones.AsParallel().ForAll( wrapper );
                }
                else if ( inParallel ) {
                    Parallel.ForEach( clones, wrapper );
                }
                else {
                    clones.ForEach( wrapper );
                }
            }
            else {
                lock ( this._items ) {
                    if ( asParallel ) {
                        this._items.AsParallel().ForAll( wrapper );
                    }
                    else if ( inParallel ) {
                        Parallel.ForEach( this._items, wrapper );
                    }
                    else {
                        this._items.ForEach( wrapper );
                    }
                }
            }
        }
    }
}
 -1
Author: Protiguous, 2012-12-23 13:17:52

Basicamente se quiser enumerar com segurança, tem de usar o lock.

Por favor consulte a MSDN sobre isto. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx

Aqui está parte da MSDN que você pode estar interessado:

Os membros estáticos públicos (partilhados no Visual Basic) deste tipo são seguros de rosca. Os membros de qualquer instância não estão garantidos para ser thread seguro.

Uma lista pode suportar vários leitores simultaneamente, desde que a colecção não seja modificada. Enumerar através de uma coleção não é intrinsecamente um procedimento de thread-safe. No caso raro em que uma enumeração se contenta com um ou mais acessos de escrita, a única maneira de garantir a segurança do fio é bloquear a coleção durante toda a enumeração. Para permitir que a coleção seja acessada por vários threads para leitura e escrita, você deve implementar sua própria sincronização.

 -3
Author: istudy0, 2011-05-03 19:06:41

Use a declaração lock para fazer isto. Leia aqui para mais informações.)

private List<T> _list;

private List<T> MyT
{
    get { return _list; }
    set
    {
        //Lock so only one thread can change the value at any given time.
        lock (_list)
        {
            _list = value;
        }
    }
}
Para que saibas, isto não é exactamente o que estás a perguntar, provavelmente queres esconder mais no teu código, mas não posso assumir isso. Veja a palavra-chave lock e adapte a sua utilização à sua situação específica.

Se for necessário, pode {[[1]} tanto no bloco get como no bloco set Usando a variável _list que faria com que uma leitura/escrita não pudesse ocorrer ao mesmo tempo.

 -11
Author: Josh M., 2011-05-03 21:03:01