スレッドセーフなList <T>プロパティ


122

私はList<T>間違いなくスレッドセーフに使用できるプロパティとしての実装を望んでいます。

このようなもの:

private List<T> _list;

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

コレクションのコピー(複製)を返す必要があるようですが、どこかでコレクションを反復処理していて、同時にコレクションが設定されている場合、例外は発生しません。

スレッドセーフなコレクションプロパティを実装する方法は?


4
ロックを使用してください。
atoMerz

IList<T>(vs List<T>)のスレッドセーフな実装を使用できますか?
グレッグ、

2
SynchronizedCollection <T>を確認しましたか?
Saturn Technologies

BlockingCollectionまたはConcurrentDictionaryを使用してください
kumar chandraketu

プロパティの背後にあるオブジェクトをどのように操作する必要がありますか?List<T>実装するものすべてが必要ない可能性はありますか?はいの場合、List<T>すでに持っているすべてのものについて尋ねるのではなく、必要なインターフェースを提供していただけませんか?
Victor

回答:


185

.Net 4をターゲットにしている場合、System.Collections.Concurrent名前空間にいくつかのオプションがあります

ConcurrentBag<T>この場合、代わりに使用できますList<T>


5
List <T>と同様に、Dictionaryとは異なり、ConcurrentBagは重複を受け入れます。
ライト

115
ConcurrentBagは順序付けられていないコレクションであるため、List<T>順序付けは保証されません。また、インデックスでアイテムにアクセスすることもできません。
RadekStromský2013年

11
@RadekStromskýは正しいです。順序付けられた同時リストが必要な場合は、ConcurrentQueue(FIFO)またはConcurrentStack(LIFO)を試すことができます。
Caio Cunha


12
ConcurrentBagはIListを実装せず、実際にはListのスレッドセーフバージョンではありません
Vasyl Zvarydchuk

87

それが最も多くの票を得たとしても、注文されていないので(RadekStromskýがすでに指摘しているように)通常はSystem.Collections.Concurrent.ConcurrentBag<T>スレッドセーフな代替System.Collections.Generic.List<T>として採用することはできません。

しかしSystem.Collections.Generic.SynchronizedCollection<T>、フレームワークの.NET 3.0の部分からすでに呼ばれているクラスがありますが、それがあまり知られていないことを予期していない場所によく隠されており、おそらくこれまでつまずいたことはないでしょう(少なくとも私は決してしませんでした)。

SynchronizedCollection<T>アセンブリSystem.ServiceModel.dllにコンパイルされます(これはクライアントプロファイルの一部ですが、ポータブルクラスライブラリの一部ではありません)。

お役に立てば幸いです。


3
私はこれがコアlibにないことを叫びます:{必要なのは、単純な同期コレクションだけです。
user2864740

このオプションに関するその他の役立つディスカッション:stackoverflow.com/a/4655236/12484
Jon Schneider

2
System.Collections.Concurrentのクラスを支持して非推奨となったため、非表示になっています。
denfromufa

3
.netコアでは利用できません
denfromufa


17

サンプルのThreadSafeListクラスを作成するのは簡単だと思います。

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;
    }
}

列挙子を要求する前にリストを複製するだけで、実行中に変更できないコピーから列挙が機能します。


1
これは浅いクローンではないですか?Tが参照型の場合、これは元のすべてのオブジェクトへの参照を含む新しいリストを返すだけではありませんか?その場合、リストのさまざまな「コピー」を介して複数のスレッドがリストオブジェクトにアクセスできるため、このアプローチでもスレッドの問題が発生する可能性があります。
Joel B

3
正解です。浅いコピーです。重要なのは、繰り返し実行しても安全なクローンセットを用意することです(そのためnewList、列挙子を無効にするアイテムの追加や削除はありません)。
Tejs

7
_lockは静的である必要がありますか?
マイクワード

4
別の考え。この実装は複数のライターに対してスレッドセーフですか?そうでない場合は、ReadSafeListと呼ぶ必要があります。
マイクワード

5
@MikeWard -私は、それがあるべきとは思わない、すべての時にインスタンスがロックされる任意のインスタンスを複製しています!
Josh M. 14年

11

受け入れられた答えはConcurrentBagですが、答えに対するRadekのコメントが言うように、私はそれがすべてのケースで実際のリストの置き換えであるとは思いません:「ConcurrentBagは順序付けられていないコレクションなので、Listとは異なり、順序付けを保証しません。 」

したがって、.NET 4.0以降を使用する場合の回避策は、配列インデックスとして整数TKeyを、配列値としてTValueを指定してConcurrentDictionaryを使用することです。これは、PluralsightのC#Concurrent Collectionsコースのリストを置き換えるための推奨される方法です。ConcurrentDictionaryは、上記の両方の問題を解決します。インデックスのアクセスと順序付け(内部ではハッシュテーブルであるため、順序付けに依存することはできませんが、現在の.NET実装では要素の追加順序が保存されます)。


1
-1の理由を
記入して

私は反対票を投じなかったし、それにIMOの理由はない。あなたは正しいですが、概念はいくつかの回答ですでに言及されています。私にとっては、私が知らなかった.NET 4.0の新しいスレッドセーフコレクションがあることがポイントでした。状況に応じて使用済みのバッグまたはコレクションが不明です。+1
ザクロン2017年

2
この回答にはいくつかの問題があります:1)ConcurrentDictionaryリストではなく辞書です。2)ご自身の回答が述べているように、順序を保持することは保証されていません。3)関連する引用をこの回答に含めずにビデオにリンクします(とにかく、ライセンスに一致しない場合があります)。
jpmc26

current implementationドキュメントで明示的に保証されていない場合などは、信頼できません。実装は予告なく変更される場合があります。
Victor Yarema

@ jpmc26、はい、もちろんこれはListの完全な置き換えではありませんが、受け入れられた回答としてConcurrentBagの場合と同じです。Listの厳密な置き換えではなく、回避策です。あなたの懸念に答えるには:1)ConcurrentDictionaryは正しいリストではなくディクショナリですが、リストの後ろには配列があり、O(1)でインデックスを作成できます。これは、intをキーとして持つディクショナリと同じです。保持されている場合でも)、ただし受け入れられたConcurrentBagは、マルチスレッドシナリオでも順序を保証できません
tytyryty

9

C#のArrayListクラスにはSynchronizedメソッドがあります。

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

これは、のすべてのインスタンスのスレッドセーフラッパーを返しますIList。スレッドの安全性を確保するには、すべての操作をラッパーを介して実行する必要があります。


1
何語を話しているの?
John Demetriou

Java?私が見逃した数少ない機能の1つ。ただし、通常は次のように記述されます。Collections.synchronizedList(new ArrayList());
ニック

2
これは、System.Collectionsを使用している場合、またはvar System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList());を使用できる場合に有効なC#です。
user2163234 2018年

5

Tのリストのソースコード(https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877)を見ると、そこにクラスがあります(もちろん、内部-なぜ、マイクロソフト、なぜ?!?!)がTのSynchronizedListを呼び出しました。ここにコードをコピーして貼り付けています。

   [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);
            }
        }
    }

個人的には、SemaphoreSlimを使用してより良い実装を作成できることを彼らは知っていたと思いますが、それには到達しませんでした。


2
+1 _root各アクセス(読み取り/書き込み)でコレクション全体()をロックすると、これは遅いソリューションになります。おそらく、このクラスは内部に留まる方が良いでしょう。
Xaqron 2018

3
この実装はスレッドセーフではありません。それでも「System.InvalidOperationException: 'コレクションが変更されました。列挙操作が実行されない可能性があります。'」を
スローします

2
これはスレッドの安全性とは関係ありませんが、コレクションを反復して変更しているという事実に関係しています。例外は、リストが変更されたことがわかると、列挙子によってスローされます。これを回避するには、独自のIEnumeratorを実装するか、同じコレクションを同時に反復および変更しないようにコードを変更する必要があります。
Siderite Zackwehdex 2018年

「同期された」メソッドの実行中にコレクション変更できるため、スレッドセーフではありません。それは絶対にあるスレッドの安全性の一部。Clear()別の呼び出しの後でthis[index]、ロックがアクティブになる前に、あるスレッドの呼び出しを検討してください。index安全に使用できなくなり、最終的に実行されると例外がスローされます。
Suncat2000

2

よりプリミティブを使用することもできます

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

どのロックが使用するか(この投稿を参照してくださいC#ロックブロックで再割り当てされるオブジェクトのロック)。

コードで例外を予期している場合、これは安全ではありませんが、次のようなことができます。

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");
        }
    }
}

これの良い点の1つは、(各操作でロックするのではなく)一連の操作の間ロックを取得することです。これは、出力が適切なチャンクで出力されることを意味します(これの私の使用法は、外部プロセスから画面にいくつかの出力を取得することでした)

クラッシュを停止する上で重要な役割を果たすThreadSafeList +のシンプルさ+透過性が本当に気に入っています


2

.NET Core(すべてのバージョン)では、のすべての機能を備えたImmutableListを使用できますList<T>


1

私は_list.ToList()あなたにコピーを作ると信じています。次のようにする必要がある場合は、クエリを実行することもできます。

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

とにかく、msdnは、これは確かにコピーであり、単なる参照ではないと言っています。ああ、そうです。他の人が指摘したように、setメソッドをロックする必要があります。


1

これを見つけた人々の多くは、スレッドセーフなインデックス付きの動的サイズのコレクションを望んでいるようです。私が知っている最も近くて簡単なことは、

System.Collections.Concurrent.ConcurrentDictionary<int, YourDataType>

これにより、通常のインデックス作成動作が必要な場合は、キーが適切に侵害されていることを確認する必要があります。注意する場合、追加する新しいキーと値のペアのキーとして.countで十分です。


1
キーのせいではなかったのに、なぜキーに罪を犯すべきなのでしょうか。
Suncat2000

@ Suncat2000 ha!
リチャードII

1

私は誰を扱うことをお勧めList<T>を見てみるために、マルチスレッドのシナリオでの不変コレクション特にImmutableArray

次の場合に非常に便利です。

  1. リスト内の比較的少数のアイテム
  2. それほど多くの読み取り/書き込み操作はありません
  3. 多数の同時アクセス(つまり、読み取りモードでリストにアクセスする多くのスレッド)

また、トランザクションのような動作を実装する必要がある場合にも役立ちます(失敗した場合は、挿入/更新/削除操作を元に戻します)。


-1

ここにあなたが求めたクラスがあります:

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 );
                    }
                }
            }
        }
    }
}

クラスを更新すると、Googleドライブのバージョンが更新されます。uberscraper.blogspot.com/2012/12/c-thread-safe-list.html
隣接

this.GetEnumerator();@Tejsが提案するのはなぜthis.Clone().GetEnumerator();ですか?
・クール

なんで[DataContract( IsReference = true )]
・クール

最新バージョンがGitHubに公開されました! github.com/AIBrain/Librainian/blob/master/Collections/...
Protiguous

Add()メソッドの2つの小さなバグを見つけて修正しました。ご参考までに。
2014

-3

基本的に安全に列挙したい場合は、ロックを使用する必要があります。

これについては、MSDNを参照してください。http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx

これは、興味があるかもしれないMSDNの一部です。

この型のpublic static(Visual BasicではShared)メンバーはスレッドセーフです。インスタンスメンバーは、スレッドセーフであるとは限りません。

コレクションが変更されない限り、リストは複数のリーダーを同時にサポートできます。コレクションを列挙することは、本質的にスレッドセーフな手順ではありません。列挙が1つ以上の書き込みアクセスと競合するまれなケースでは、スレッドの安全を確保する唯一の方法は、列挙全体でコレクションをロックすることです。複数のスレッドがコレクションにアクセスして読み書きできるようにするには、独自の同期を実装する必要があります。


2
まったくそうではありません。並行セットを使用できます。
ANeves

-3

ロックのないスレッドセーフリストのクラスは次のとおりです

 public class ConcurrentList   
    {
        private long _i = 1;
        private ConcurrentDictionary<long, T> dict = new ConcurrentDictionary<long, T>();  
        public int Count()
        {
            return dict.Count;
        }
         public List<T> ToList()
         {
            return dict.Values.ToList();
         }

        public T this[int i]
        {
            get
            {
                long ii = dict.Keys.ToArray()[i];
                return dict[ii];
            }
        }
        public void Remove(T item)
        {
            T ov;
            var dicItem = dict.Where(c => c.Value.Equals(item)).FirstOrDefault();
            if (dicItem.Key > 0)
            {
                dict.TryRemove(dicItem.Key, out ov);
            }
            this.CheckReset();
        }
        public void RemoveAt(int i)
        {
            long v = dict.Keys.ToArray()[i];
            T ov;
            dict.TryRemove(v, out ov);
            this.CheckReset();
        }
        public void Add(T item)
        {
            dict.TryAdd(_i, item);
            _i++;
        }
        public IEnumerable<T> Where(Func<T, bool> p)
        {
            return dict.Values.Where(p);
        }
        public T FirstOrDefault(Func<T, bool> p)
        {
            return dict.Values.Where(p).FirstOrDefault();
        }
        public bool Any(Func<T, bool> p)
        {
            return dict.Values.Where(p).Count() > 0 ? true : false;
        }
        public void Clear()
        {
            dict.Clear();
        }
        private void CheckReset()
        {
            if (dict.Count == 0)
            {
                this.Reset();
            }
        }
        private void Reset()
        {
            _i = 1;
        }
    }

これはスレッドセーフではありません
Aldracor

_i ++はスレッドセーフではありません。それをインクリメントするときはアトミック追加を使用する必要があり、おそらく揮発性にもマークを付けます。CheckReset()はスレッドセーフではありません。条件チェックとReset()の呼び出しの間に何かが起こる可能性があります。独自のマルチスレッドユーティリティを作成しないでください。
クリスロリンズ

-15

lockこれを行うには、ステートメントを使用します。(詳細については、こちらをご覧ください。

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;
        }
    }
}

ちなみに、これはおそらくあなたが求めているものとは正確には同じではありません。コードをさらにロックアウトしたいと思うかもしれませんが、私はそれを想定できません。見てくださいlockキーワードをて、特定の状況に合わせてその使用を調整します。

必要な場合はlockgetsetブロックの両方で_list変数を使用して、読み取り/書き込みが同時に行われないようにすることができます。


1
それで彼の問題が解決するわけではありません。スレッドが参照を設定しないようにするだけで、リストには追加されません。
Tejs、2011年

そして、あるスレッドが値を設定している間に別のスレッドがコレクションを反復している場合はどうでしょう(コードで可能です)。
Xaqron、2011年

私が言ったように、ロックはおそらくコード内でさらに移動する必要があります。これは、lockステートメントの使用例です。
Josh M.

2
@ジョエル・ミュラー:もちろん、あなたがそのような愚かな例を製造しているなら。私は、質問者がlock声明を調べるべきであることを説明しようとしています。あなたはほとんど労力でアプリケーションをデッドロック可能性があるため、私たちはループのために使うべきではないと主張している可能性が同様の例を使用して:for (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
ジョシュ・M.

5
私はあなたのコードがインスタントデッドロックを意味するとは決して主張しませんでした。次の理由により、この特定の質問に対する悪い回答です。1)リストの列挙中に、または一度に2つのスレッドによってリストの内容が変更されるのを防ぎません。2)ゲッターではなくセッターをロックすると、プロパティは実際にはスレッドセーフではありません。3)上でロック任意それが劇的に偶発的デッドロックの可能性を増加させるように、クラス外部からアクセス可能であるが広く、悪い習慣と考えられている参照。それが理由でlock (this)ありlock (typeof(this))、大きな禁止事項です。
Joel Mueller、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.