2011-07-17 12 views
1

以下のクラスでは、1つのスレッドがIEnumerableオブジェクトを取得し、要素に対して反復処理を開始するとします。反復の途中で、別のスレッドが追加され、Add-methodを介してlibrary_entriesに新しいエントリを追加します。反復で "コレクションが変更されました" - 例外がスローされるのでしょうか?または、反復が完了するまで、ロックによって要素の追加が防止されますか?それともどちらですか?スレッディングとIEnumerable; "コレクションが変更されました" -exception

ありがとうございます!

public static class Library 
{ 
    private static List<string> library_entries = new List<string>(1000000); 

    public static void Add(string entry) 
    { 
     lock (library_entries) 
      library_entries.Add(entry); 
    } 

    public static IEnumerable<string> GetEntries() 
    { 
     return library_entries.Where(entry => !string.IsNullOrEmpty(entry)); 
    } 
} 
+1

.net 4の並行コレクションを調べて、それらのうちの1つがあなたがしたいことに役立つかどうかを調べることができます(私はあなたが望むものについてはわかりません)。 – CodesInChaos

答えて

0

静的GetEntriesメソッドがstatic library_entriesコレクション上の任意のロックを実行していない=>それはスレッドセーフではなく、複数のスレッドから、それ上の任意の同時通話が壊れるかもしれません。 Addメソッドをロックしても問題ありませんが、列挙はスレッドセーフな操作ではないため、GetEntriesメソッドを同時に呼び出す場合は、ロックする必要があります。また、このメソッドはIEnumerable<T>を返しますので、GetEntriesメソッドの外にある可能性がある列挙を開始するまで、実際のリストでは何も行いません。したがって、LINQチェーンの末尾に.ToList()コールを追加し、操作全体をロックすることができます。

1

ロックはロックを使用しないため、ロックはまったく役に立ちません。コピーを返すようにGetEntries()関数を書き換えることをお勧めします。

public static IEnumerable<string> GetEntries() 
{ 
    lock(lockObj) 
    { 
     return library_entries.Where(entry => !string.IsNullOrEmpty(entry)).ToList(); 
    } 
} 

これは一貫したスナップショットを返します。つまり、反復処理中に新たに追加されたオブジェクトを返しません。

私はプライベートオブジェクトをロックすることを好みますが、その目的はロックされていますが、リストはプライベートなので実際の問題ではなく、文句の問題です。 ...あなたはまた、使用する場合がありますそれは良いアイデアだ場合

int i=0; 
bool MoveNext() 
{ 
    lock(lockObj) 
    { 
     if(i<list.Count) 
      return list[i]; 
     i++; 
    } 
} 

、お使いのアクセスパターン、ロックの競合、リストのサイズによって異なります。あなたはまた、次のように独自のイテレータを書くことができ

は、多くの読み取りアクセスからの競合を避けるために、読み取り - 書き込みロック。

+0

コピーを返すことで、遅延実行の利点が失われます。 – Femaref

+0

はい、その方法で遅延実行が失われ、高価な割り当てが行われる可能性があります。代わりに、ある種のスレッドセーフなイテレータを考案することもできますが、それが良い考えであるかどうかはわかりません。 – CodesInChaos

0

はい、例外がスローされます。つまり、共通オブジェクトをロックしていません。また、GetEntriesメソッドのロックは、その呼び出しが瞬時に戻りますので、役に立たなくなります。反復処理中にロックが発生するはずです。

4

いいえ、あなたはLinqクエリを使用していますが、例外はありません。それはずっと悪く、予期せず失敗するでしょう。最も一般的な結果は、IndexOutOfRangeExceptionを含むAdd()呼び出し中にListが内部ストレージを再割り当てするときには何も可能ですが、同じ項目が2回列挙されるということです。一週間に一度、与えてください。

GetEntries()を呼び出して列挙子を使用するコードでも、ロックを取得する必要があります。 Where()式のロックは十分ではありません。あなたがリストのコピーを作成しない限り。