0

基本的に私が必要とするのは、1 つのスレッドで 1 秒あたり約 3000 回、リスト (または別のコレクション) にアイテムを常に追加できるようにすることです。そして、2 秒に 1 回、そのリストからすべての項目を取得して削除します。

並行コレクションを使用したり、コレクションにアクセスする必要があるたびに何かをロックしたりするなど、これを行う従来の方法は好きではありません。必要以上に遅くなるためです。

私がやろうとしているのは、スレッドごとに 1 つずつ、2 つのコレクションを持ち、あるコレクションから別のコレクションにスレッドセーフに切り替える方法を見つけることです。

簡略化された、スレッドセーフではない例:

var listA = new List<int>();
var listB = new List<int>();

// method is called externally 3000 times per second
void ProducerThread(int a)
{      
    listA.Add(a)      
}
void ConsumerThread()
{
  while(true)
  {
    Thread.Sleep(2000);
    listB = Interlocked.Exchange(ref listA,listB);
    //... processing listB data
    // at this point when i'm done reading data
    // producer stil may add an item because ListA.Add is not atomic
    // correct me if i'm wrong
    listB.Clear();
  }
}

プロデューサースレッドのブロックをできるだけ少なくしながら、上記のコードを意図したとおりに (スレッドセーフにするために) 動作させる方法はありますか? それとも別の解決策ですか?

4

3 に答える 3

2

System.Collections.Concurrent でaBlockingCollectionまたは anotherを使用することから始めます。IProducerConsomerCollectionそれはまさにあなたが持っているものであり、複数のスレッドからアクセスされる生産者/消費者キューです。これらのコレクションは、パフォーマンスのために大幅に最適化されています。彼らは、単純な「誰かが操作を行うたびに構造全体をロックする」という単純な方法を使用しません。それらは、ロックフリー同期技術を使用して可能な限りロックを回避するのに十分スマートであり、クリティカルセクションを使用する必要がある場合は、ロックする必要があるものを最小限に抑えて、一定量のロックにもかかわらず構造に同時にアクセスできるようにします。

そこから別の場所に移動する前に、これらのコレクションの 1 つを使用し、それが遅すぎることを確認します。それを解決策として使用した後、コレクションにアイテムを追加/削除するのに許容できない時間を費やしていることを示した場合は、他の解決策を調査す​​ることを検討できます。

私がそうなると思うのですが、それらが十分に速く実行されれば、コードの記述がはるかに簡単になり、読みやすくなることに気付くでしょう。

于 2012-10-16T17:38:29.907 に答える
1

への新しい追加を処理したいだけでlistA、これらの追加を処理している間にさらに追加が行われると想定しています。

var listA = new List<int>();
var dictA = new Dictionary<int,int>();

int rangeStart = 0;
int rangeEnd = 0;
bool protectRange = false;

// method is called externally 3000 times per second
void ProducerThread(int a)
{      
 listA.Add(a);
 dictA.Add(rangeEnd++,a);   
}
void ConsumerThread()
{
 while(true)
 {
  Thread.Sleep(2000);
  int rangeInstance = rangeEnd;
  var listB = new List<int>();
  for( int start = rangeStart; start < rangeInstance; start++ ){
   listB.add(dictA[start]);
   rangeStart++;
  }
  //... processing listB data
  }
}
于 2012-10-16T17:56:50.940 に答える
0

テーブルの最大サイズが固定されている場合、なぜリストを使用するのでしょうか? リストのサイズをあらかじめ設定することもできます。

List<int> listA = new List<int>(6000);

今、私は実際に以下をテストしていませんが、あなたが望むことをすると思います:

int[] listA = new int[6000]; // 3000 time * 2 seconds
int i = 0;

// method is called externally 3000 times per second
void ProducerThread(int a)
{
    if (Monitor.TryEnter(listA)) // If true, consumer is in cooldown.
    {
        listA[i] = a;
        i++;
        Monitor.Exit(listA);
    }
}

void ConsumerThread()
{
    Monitor.Enter(listA); // Acquire thread lock.

    while (true)
    {
        Monitor.Wait(listA, 2000); // Release thread lock for 2000ms, automaticly retake it after Producer released it.

        foreach (int a in listA) { } //Processing...

        listA = new int[6000];
        i = 0;
    }
}

ConsumerThread が最初に実行されていることを確認する必要があるだけなので、それ自体がキューに入れられて待機します。

于 2012-10-16T17:25:20.333 に答える