BlockingCollection を ConcurrentDictionary と一緒に使用している場合は、コードのどこかに BlockingCollection.TryAdd(myobject) メソッドが隠されておらず、それを ConcurrentDictionary.TryAdd() メソッドと間違えていないことを確認してください。BlockingCollection.TryAdd(myobject) は、BlockingCollection の境界容量を超えた場合、false を返し、追加要求を破棄して「サイレント フェイル」を生成します。
- BlockingCollection の .Add() メソッドは、バウンディング キャパシティを大幅に超えた後に「サイレント フェイル」したり、追加を失ったりするようには見えません。add() メソッドは、容量を超えた BlockingCollection への追加を待機している .add() が多すぎる場合、最終的にプロセスのメモリ不足を引き起こします。(これは、フロー制御の問題の非常に極端なケースでなければなりません)
- #1を参照してください。
- 私自身のテストでは、MSDN ドキュメントで説明されているように、CompleteAdding() メソッドが呼び出されると、後続のすべての追加が失敗することが示されているようです。
パフォーマンスに関する最後の注意事項
(私の場合はとにかく) BlockingCollection で Bounding Capacity と .Add() を使用すると、同じプロセスで Bounding Capacity と .TryAdd() を使用しない場合に比べて非常に遅いようです。
独自の境界容量戦略を実装することで、はるかに優れたパフォーマンス結果を達成しました。これを行うには多くの方法があります。3 つの選択肢には、Monitor.PulseAll() と一緒に使用される Thread.Sleep()、Thread.Spinwait()、または Monitor.Wait() が含まれます。これらの戦略のいずれかを使用する場合、BlockingCollection.Add() の代わりに BlockingCollection.TryAdd() を使用することもでき、データを失うこともメモリ不足になることもなく、境界容量を持たないこともできます。この方法もパフォーマンスが向上するようです。
Producer スレッドと Consumer スレッドの速度の違いに最適なシナリオに基づいて、3 つの例から選択できます。
Thread.Wait() 例:
//Check to see if the BlockingCollection's bounded capacity has been exceeded.
while (Tokens.Count > 50000)
{ //If the bounded capacity has been exceeded
//place the thread in wait mode
Thread.Sleep(SleepTime);
}
Thread.SpinWait() 例:
//Check to see if the BlockingCollection's bounded capacity has been exceeded.
while (Tokens.Count > 50000)
{ //If the capacity has been exceeded
//place the thread in wait mode
Thread.SpinWait(SpinCount);
}
Monitor.Wait() の例
この例では、Producer 側と Consumer 側の両方にフックが必要です。
プロデューサーコード
//Check to see BlockingCollection capacity has been exceeded.
if (Tokens.Count > 50000)
{
lock (syncLock)
{ //Double check before waiting
if (Tokens.Count > 50000)
{
Monitor.Wait(syncLock, 1000);
}
}
}
消費者コード
//Check to see BlockingCollection capacity is back a normal range.
if (Tokens.Count <= 40000)
{
lock (syncLock)
{ //Double check before waiting
if (Tokens.Count < 40000)
{
Monitor.PulseAll(syncLock);
}
}
}