2011-10-29 3 views
0

私は、マルチスレッド形式のアプリケーションを持っており、これは、問題の一部が設計された方法です。ManualResetEvent - WAITONE()はいくつかの点でスレッドを解放していないようだ

スレッド2(BatchPreviewAssistantクラス)を待っていますプライマリインターフェイスのスレッドは、画像を読み込むタスクを渡す。タスクが受信されると、BatchPreviewAssistantはタスクをN = 5 waiting PrimaryLoaderスレッドに割り当て、それらを有効にします。 PrimaryLoaderは、_startEventと_endEventの2つの手動リセットイベントを使用して開始/停止された無限ループとして実行されています。また、PrimaryLoadersからBatchPreviewAssistantへの処理の終了を知らせるためのN個の手動リセットイベント_parentSyncEventの配列があります。

通常、各PrimaryLoaderは_startEvent.WaitOne()で待機しています。 BatchPreviewAssistantがそれらをアクティブにしてRunPrimaryLoaders()を実行すると、まず_endEventおよび_parentSyncEventsをリセットしてから_startEventを設定します。 各PrimaryLoaderが完了すると、すべての5が設定されるまで_ParentSyncEventに独自のイベントが設定されます。この時点で、すべてのPrimaryLoaderは_endEventに到達します。 WAITONE()と待っ今_parentSyncEventsは継続するBatchPreviewAssistantを可能にし、すべてのセットです はBatchPreviewAssistantは_startEventをリセットしてからPrimaryLoadersを解放_endEventを設定し、彼らは、ループの最初に戻ってくる

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()); 
    } 

PrimaryLoader:

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()を設定すると、PrimaryLoadersのどれもが_endEvent.WaitOne()で解放されません。 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 

このようなデザインで問題を引き起こす可能性のある問題はありますか? 私は回避策として試してみる方法がいくつかありますが、この問題が何か問題であるかどうかは分かります。

プライマリローダーを初期化して起動する方法についてもinformatinを提供しています。私はサンプルがあまりにも忙しいとフォローするのは困難であることを同意するだろう :

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 
    } 
} 
+0

あなたは競争状態を持っているようです... TTジョークを除いて、あなたの例はあまりにも長く、従うのが複雑です。デザインの実際の目的は何ですか? – Kiril

答えて

2

は、問題を引き起こす可能性があり、このようなデザインでどんなabviousの問題はありますか?

シンプルなことをしようとすると非常に複雑なデザインが登場するようです。単純なプロデューサー/コンシューマーのパターンはもっとうまくいくと思われ、この手動リセットイベントの災害に対処する必要はありません。

class Producer 
{ 
    private readonly BlockingQueue<Task> _queue; 

    public Producer(BlockingQueue<Task> queue) 
    { 
     _queue = queue; 
    } 

    public LoadImages(List<Task> imageLoadTasks) 
    { 
     foreach(Task t in imageLoadTasks) 
     { 
      _queue.Enqueue(task); 
     } 
    } 
} 

class Consumer 
{ 
    private volatile bool _running; 
    private readonly BlockingQueue<Task> _queue; 

    public Consumer(BlockingQueue<Task> queue) 
    { 
     _queue = queue; 
     _running = false; 
    } 

    public Consume() 
    { 
     _running = true; 

     while(_running) 
     { 
      try 
      { 
       // Blocks on dequeue until there is a task in queue 
       Task t = _queue.Dequeue(); 

       // Execute the task after it has been dequeued 
       t.Execute(); 
      } 
      catch(ThreadInterruptedException) 
      { 
       // The exception will take you out of a blocking 
       // state so you can check the running flag and decide 
       // if you need to exit the loop or if you shouldn't. 
      } 
     } 
    } 
} 

だからあなたも独自のスレッドに別のスレッド上の各Producerインスタンスと各Consumerインスタンスを実行する必要があります:

はおそらく、この線に沿ってより多くの何かをしたいです。もちろん、あなたは優雅にそれらを終了するためにすべての鐘とホイッスルを追加する必要がありますが、それは別の話です。

0

あなたは競合状態にあります。あなたのロジックが条件を検出し、ブロックするイベントを設定し、イベントを待っている場合は、介入するロックがなければなりません。

あなたのコードをこのん:

  1. は、イベントが発生した場合に問題が発生したイベント

  • 待ちをブロックするために

  • セットのイベントを待つことにしましたステップ1と2の間で、イベントが発生している可能性があります。イベントをブロックするように設定したときにイベントをckedします。ステップ3に進むと、既にブロックされていないオブジェクトのブロックを解除するために既に発生したイベントを待っています。悪い。

    修正は次のとおりです。

    1. 獲得ロック

    2. 我々は待つ必要がありますか?いいえ、レリーズロックとは、我々は今、ロックを保持しているのでイベント

  • リリースロック

  • 待ちをブロックするために

  • セットのイベントを返した場合、イベントはの間に発生することはできません私たちが待つことを決定したとき、そしてイベントをブロックするように設定したとき。イベントをブロック解除するコードは、もちろん、イベントを処理し、イベントのブロックを解除するロジックと同じロックを保持する必要があります。

  • 関連する問題