私はマルチスレッドフォームアプリケーションを持っています.これは問題の部分がどのように設計されているかです:
スレッド 2 (BatchPreviewAssistant クラス) は、プライマリ インターフェイス スレッドが画像の読み込みタスクを渡すのを待機しています。タスクが受信されると、BatchPreviewAssistant は N=5 の待機中の PrimaryLoader スレッドにタスクを割り当てて有効にします。PrimaryLoaders は、_startEvent と _endEvent の 2 つの手動リセット イベントを使用して開始/停止された無限ループとして実行されています。また、N 個の手動リセット イベント _parentSyncEvent の配列があり、PrimaryLoaders から BatchPreviewAssistant への処理の終了を通知します。
したがって、通常、各 PrimaryLoader は _startEvent.WaitOne() で待機しています。BatchPreviewAssistant がそれらをアクティブにする必要があり、RunPrimaryLoaders() を実行すると、最初に _endEvent と _parentSyncEvents がリセットされ、次に _startEvent が設定されます。現在、WaitHandle.WaitAll(_parentSyncEvents) でブロックされます。_startEvent.Set() により、すべての PrimaryLoader が続行されます。各 PrimaryLoader が完了すると、5 つすべてが設定されるまで、_parentSyncEvent に独自のイベントが設定されます。この時点で、すべての PrimaryLoader が _endEvent.WaitOne(これで、_parentSyncEvents がすべて設定され、BatchPreviewAssistant が続行できるようになります。BatchPreviewAssistant は _startEvent をリセットし、_endEvent を設定して PrimaryLoaders を解放し、ループの先頭に戻ります。
BatchPreviewAssistant:
private void RunPrimaryLoaders()
{
BatchPreviewThreadsLogger.WriteLog(Common.LogLevel.Debug1, "RunPrimaryLoaders()");
ResetEvents(_parentSyncEvents);
_endEvent.Reset();
_startEvent.Set();
// Primary Loader loops restart
BatchPreviewThreadsLogger.WriteLog(Common.LogLevel.Debug2, "WaitHandle.WaitAll(_parentSyncEvent");
if (!WaitHandle.WaitAll(_parentSyncEvents, 20 * 1000))
{
throw new TimeoutException("WaitAll(_parentSyncEvent) in ProcessCurrentCommand");
// TODO: Terminate?
}
BatchPreviewThreadsLogger.WriteLog(Common.LogLevel.Message3, "Primary loading is complete");
_startEvent.Reset();
_endEvent.Set();
bool isEndEventSet = _endEvent.WaitOne(0);
BatchPreviewThreadsLogger.WriteLog(Common.LogLevel.Debug2, "isEndEventSet?" + isEndEventSet.ToString());
}
プライマリローダー:
public void StartProc(object arg)
{
while (true)
{
BatchPreviewThreadsLogger.WriteLog(Common.LogLevel.Debug2, "Primary Loader: _startEvent.WaitOne()");
_startEvent.WaitOne();
try
{
BatchPreviewThreadsLogger.WriteLog(Common.LogLevel.Message4, "Primary Loader is processing entry:" + processingEntry.DisplayPosition.ToString());
}
catch (Exception ex)
{
BatchPreviewThreadsLogger.WriteLog(Common.LogLevel.Error, "Exception in PrimaryImageLoader.StartProc:" + ex.ToString());
}
_parentSyncEvent.Set();
BatchPreviewThreadsLogger.WriteLog(Common.LogLevel.Debug2, "Primary Loader: _endEvent.WaitOne()");
_endEvent.WaitOne();
}
}
このコードは、このようなループを何百回も作成するのに非常にうまく機能しますが、特にストレステスト中に、時々問題が発生します。BatchPreviewAssistant が _endEvent.Set() を設定すると、_endEvent.WaitOne(); でどの PrimaryLoader も解放されません。BatchPreviewAssistant をチェックインすると、イベントが実際に設定されていることがわかりますが、PrimaryLoaders は解放されていません。
[10/27/2011;21:24:42.796;INFO ] [42-781:16]Primary Loader: _endEvent.WaitOne()
[10/27/2011;21:24:42.796;INFO ] [42-781:18]Primary Loader: _endEvent.WaitOne()
[10/27/2011;21:24:42.796;INFO ] [42-781:19]Primary Loader: _endEvent.WaitOne()
[10/27/2011;21:24:42.843;INFO ] [42-843:15]Primary Loader: _endEvent.WaitOne()
[10/27/2011;21:24:42.937;INFO ] [42-937:17]Primary Loader: _endEvent.WaitOne()
[10/27/2011;21:24:42.937;INFO ] [42-937:14]Primary loading is complete
[10/27/2011;21:24:42.937;INFO ] [42-937:14]isEndEventSet?True
このような設計に問題を引き起こす可能性のある明らかな問題はありますか? 回避策としていくつかの方法を試すことができますが、このアプローチの何が問題なのかを確認できれば幸いです。
念のため、PrimaryLoaders を初期化して開始する方法についても情報を提供しています。
private PrimaryImageLoader[] _primaryImageLoaders;
_primaryImageLoaders = new PrimaryImageLoader[N]
for (int i = 0; i < _primaryImageLoaderThreads.Length; i++)
{
_parentSyncEvents[i] = new AutoResetEvent(false);
_primaryImageLoaders[i] = new PrimaryImageLoader(i, _parentSyncEvents[i],
_startEvent, _endEvent,
_pictureBoxes, _asyncOperation,
LargeImagesBufferCount);
_primaryImageLoaderThreads[i] = new Thread(new ParameterizedThreadStart(_primaryImageLoaders[i].StartProc));
_primaryImageLoaderThreads[i].Start();
}
サンプルを簡略化するために、無関係なコードが削除されていることに注意してください。
追加: サンプルが忙しすぎて、理解するのが難しいことに同意します。つまり、これは一言で言えば次のとおりです。
Thread 2:
private void RunPrimaryLoaders()
{
_endEvent.Reset();
_startEvent.Set();
_startEvent.Reset();
_endEvent.Set();
bool isEndEventSet = _endEvent.WaitOne(0);
}
Threads 3-7:
public void StartProc(object arg)
{
while (true)
{
_startEvent.WaitOne();
_endEvent.WaitOne(); // This is where it can't release occasionally although Thread 2 checks and logs that the event is set
}
}