私はエントリが更新され、2つの異なるスレッドから新しいデータが挿入または削除されるリストを持っています。2つの異なるスレッドのリストを使用していますか?
公開されている読み取り専用オブジェクトを使用して、ロックされているかどうか、または2つのスレッド全体でこのリストを使用する正しい方法は何ですか?
私はエントリが更新され、2つの異なるスレッドから新しいデータが挿入または削除されるリストを持っています。2つの異なるスレッドのリストを使用していますか?
公開されている読み取り専用オブジェクトを使用して、ロックされているかどうか、または2つのスレッド全体でこのリストを使用する正しい方法は何ですか?
異なるスレッドのリストにアクセスするときは、常にlock
を使用する必要があります。
public class Sample
{
object synch = new object();
List<Something> list = new List<Something>();
void Add(Something something)
{
lock (synch) { list.Add(something); }
}
// Add the methods for update and delete.
}
あなたはあなたのためのロックを処理するクラスでこれをラップする、またはそのようなConcurrentQueue<T>
やSystem.Collections.Concurrent
内の他のコレクションの一つとして、スレッドセーフなコレクションを、使用する必要があります。
同期オブジェクトを公開APIに公開するのは危険であり、良い方法ではありません。
まず、それは悪い理由を理解するために、この記事を読んで:私が行ったようhttp://blogs.msdn.com/b/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx
その後は、とにかくそれを実行します。
public abstract class ConcurrentCollection<T> : ICollection<T>
{
private List<T> List { get; set; }
public ConcurrentCollection()
{
this.List = new List<T>();
}
public T this[int index]
{
get
{
return this.List[index];
}
}
protected virtual void AddUnsafe(T item)
{
this.List.Add(item);
}
protected virtual void RemoveUnsafe(T item)
{
this.List.Remove(item);
}
protected virtual void ClearUnsafe()
{
this.List.Clear();
}
public void Add(T item)
{
lock (this.List)
{
this.AddUnsafe(item);
}
}
public bool Remove(T item)
{
lock (this.List)
{
this.RemoveUnsafe(item);
return true;
}
}
public void Clear()
{
lock (this.List)
{
this.ClearUnsafe();
}
}
public int Count
{
get
{
lock (this.List)
{
return this.List.Count;
}
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public bool Contains(T item)
{
lock (this.List)
{
return this.List.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (this.List)
{
this.List.CopyTo(array, arrayIndex);
}
}
public IEnumerator<T> GetEnumerator()
{
return new ConcurrentEnumerator<T>(this.List, this.List);
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException("Abstract concurrent enumerators not implemented.");
}
}
public class ConcurrentEnumerator<T> : IEnumerator<T>
{
private int Position = -1;
private List<T> Duplicate;
private object Mutex;
private ICollection<T> NonConcurrentCollection;
internal ConcurrentEnumerator(ICollection<T> nonConcurrentCollection, object mutex)
{
this.NonConcurrentCollection = nonConcurrentCollection;
this.Mutex = mutex;
lock (this.Mutex)
{
this.Duplicate = new List<T>(this.NonConcurrentCollection);
}
}
public T Current
{
get
{
return this.Duplicate[this.Position];
}
}
object IEnumerator.Current
{
get
{
return this.Current;
}
}
public bool MoveNext()
{
this.Position++;
lock (this.Mutex)
{
while (this.Position < this.Duplicate.Count && !this.NonConcurrentCollection.Contains(this.Current))
{
this.Position++;
}
}
return this.Position < this.Duplicate.Count;
}
public void Reset()
{
this.Position = -1;
}
public void Dispose() { }
}
// Standards have List as derived Collection...
public class ConcurrentList<T> : ConcurrentCollection<T> { }
このコードは、カウントの例では、例えば、まだ完全に安全でありではありません依然としてクラッシュしますが、繰り返し処理、スレッド間の追加と削除が可能です。ミューテックスを公開したい場合は、countとcontainsのような他のコード構造のためにmutexをロックしてください。
しかし、それでもやはり悪い考えです。
:使用例。
ConcurrentList<string> list = new ConcurrentList<string>();
list.Add("hello");
list.Add("world");
list.Add("foo");
list.Add("bar");
foreach (string word in list)
{
if (word == "world")
{
list.Remove("bar"); // Will not crash the foreach!
}
Console.WriteLine(word);
}
出力:
hello
world
foo
-1 OPは、エントリがリストで更新することができると述べているからです。これはConcurrentQueueを使用しています。 –
@リチャード:アルゴリズムが何をしているかによって、remove + addは完全に受け入れられるかもしれません。それは、スレッディングシナリオでアイテムを「置き換える」と言われているのは、独自の難点があります。 –