1

.NET フレームワーク 3.5

同じジェネリック コレクションを使用する 2 つのスレッドがあります。foreach次のステートメントを使用して、1 つのスレッドがコレクションをループします。

while(HaveToContinue)
{
   // Do work 1

   try
   {
      foreach(var item in myDictionary)
      {
         // Do something with/to item
      }

      // Do work 2 (I need to complete the foreach first)
   }
   catch(InvalidOperationException)
   {
   }
}

同時に、他のスレッドがコレクションを変更します。

// The following line causes the InvalidOperationException (in the foreach)
myDictionary.Remove(...);

それで、これを避ける方法はありInvalidOperationExceptionますか?この例外を回避できれば、作業 (作業 1 + 作業 2) を常に完了できますが、例外をキャッチするたびに作業を完了できません。

ManualResetEvent次のようなオブジェクトを使用することを考えました。

while(HaveToContinue)
{
   // Do work 1

   try
   {
      myResetEvent.Reset();
      foreach(var item in myDictionary)
      {
         // Do something with/to item
      }
      myResetEvent.Set();

      // Do work 2 (I need to complete the foreach first)
   }
   catch(InvalidOperationException)
   {
   }
}

そして、他のスレッドがコレクションを変更するたびに:

// Expect the foreach is completed
myResetEvent.WaitOne();
// And then modify the collection
myDictionary.Remove(...);

しかし、おそらくもっと良い解決策があります。

4

1 に答える 1

1

.NET 4 を使用している場合は、スレッドセーフなConcurrentBagorConcurrentDictionaryクラスを代わりに使用する必要があります。

以前のバージョンを使用している場合、最も簡単な (非効率ではありますが) 解決策は、lock. これをプレーンなオブジェクトとしてインスタンス化できます:

private readonly object sync = new object();

次に、リストにアクセスする必要がある場合は、最初にロックを取得します。

while (HaveToContinue)
{
   // Do work 1

   lock (sync)
   {
      foreach (var item in myDictionary)
      {
         // Do something with/to item
      }
   }

   // Do work 2 (I need to complete the foreach first)
}

同様に、コレクションを変更して同じロックを取得する場合:

lock (sync)
{
    myDictionary.Remove(...);
}

各アイテムで実行する必要がある作業量が多い場合は、最初に辞書のローカル コピーを取得し、ロックを解除してから、そのコピーを反復処理し、レーシング スレッドが実行できるようにする方が効率的です。グローバル ディクショナリを変更します。

while (HaveToContinue)
{
   // Do work 1

   Dictionary<Key,Value> localDictionary;

   lock (sync)
   {
      localDictionary = new Dictionary<Key,Value>(myDictionary);
   }

   foreach (var item in localDictionary)
   {
      // Do something with/to item
   }

   // Do work 2 (I need to complete the foreach first)
}
于 2012-11-20T09:59:19.127 に答える