2012-03-05 38 views
1

2つのスレッドのメッセージキューを実装したいと思います。スレッド#1はメッセージをキューにポップして処理します。スレッド#2は、メッセージをキューにプッシュします。WaitForSingleObject()関数とSetEvent()関数を同期させるアルゴリズムはありますか?

はここに私のコードです:SetEvent(hWakeUpEvent)WaitForSingleObject()前に呼び出される場合があります

Thread #1 //Pop message and process 
{ 
    while(true) 
    { 
     Lock(mutex); 
     message = messageQueue.Pop(); 
     Unlock(mutex); 

     if (message == NULL) //the queue is empty 
     { 
      //assume that the interruption occurs here (*) 
      WaitForSingleObject(hWakeUpEvent, INFINITE); 
      continue; 
     } 
     else 
     { 
      //process message 
     } 
    } 
} 

Thread #2 //push new message in queue and wake up thread #1 
{ 
    Lock(mutex); 
    messageQueue.Push(newMessage) 
    Unlock(mutex); 

    SetEvent(hWakeUpEvent); 

} 

問題がある(注(*))、それは危険なことでしょう。

私はそれを修正しようとしましたが、まだ解決していません。

誰かがこの問題を解決するアルゴリズムを持っている場合は、私にお尋ねください。

+1

同時境界キューを検索します。それは単にあなたの問題になります。 – Nawaz

+0

「危険でしょうか?」とはどういう意味ですか? – Tudor

+4

あなたが言うことは問題ではないと思います。なぜなら、SetEventが最初に呼び出されると、WaitForSingleObjectはまったくスリープ状態にならずすぐに戻り、ループが次の繰り返しを続けるからです。 msdnを参照 "手動リセットイベントオブジェクトの状態は、ResetEvent関数によって明示的に非シグナリング状態に設定されるまで通知されます。オブジェクトの状態が通知されている間に待機機能を解放することができます。 – svenfx

答えて

3

コードは問題ありません。

SetEventとWaitForSingleObjectの間に実際の問題はありません。重要な問題は、イベントのWaitForSingleObjectがイベントの状態をチェックし、トリガーされるまで待機することです。イベントが既にトリガーされている場合は、すぐに戻ります。これは、WaitForSingleObjectの呼び出しの前または途中でSetEventが呼び出された場合に問題ないことを意味します。どちらの場合でもWaitForSingleObjectが返されます。直ちにまたはSetEventが後で呼び出されたときに呼び出されます。

ここで自動リセットイベントを使用していると仮定しています。手動リセットイベントを使用する正当な理由は考えられません.WaitForSingleObjectが返された直後にResetEventを呼び出す必要があります。あなたが既に忘れてしまったイベントを待っているのですが、また、元のデータ状態をチェックする前にリセットすることが重要です。 Reset()が呼び出されると、その情報は失われます。自動リセットに固執し、あなたはこのすべてを避ける)

-

[編集:私は、各ウェイクにシングル「ポップ」をやって、だけではなく、空の上で待っているようOPのコードを読み違えるので以下のコメントはそのシナリオのコードを参照しています。 OPのコードは実際には以下の2番目の修正案と同等です。下のテキストは実際には、OPの実際のコードではなく、セマフォであるイベントが使用される多少の一般的なコーディングエラーを記述しています。

しかし、ここには別の問題がありますWin32イベントオブジェクトにはunsignaledとsignaledという2つの状態しかないので、バイナリ状態を追跡するために使うことはできますが、カウントすることはできません。既に通知されたSetEventとイベントは、Signaledのままであり、その余分なSetEvent呼び出しの情報は失われます。その場合

、何が起こる可能性はある:

  • 項目は、追加されると呼ばれるSetEventは、イベントは現在通知されます。
  • 別の項目が追加され、SetEventが再度呼び出され、イベントが通知されたままになります。
  • ワーカースレッドがイベントをクリアし、返しのWaitForSingleObjectを呼び出し、
  • は唯一つの項目は
  • ワーカースレッドは、イベントがまだキュー内の項目がありますにもかかわらず、unsignaledであるため、ブロックのWaitForSingleObjectを呼び出し、処理されます。

古典的なComp.Sciの方法は、イベントの代わりにセマフォを使用することです - セマフォは基本的にすべての 'Set'コールをカウントするイベントです。逆にイベントをセマフォとして考えることができます。最大カウントは1で、それ以外の信号は無視されます。

もう1つの方法はイベントを継続することですが、ワーカースレッドが起動するときには、のいくつかの項目がキューにあると仮定でき、待機する前にそれらをすべて処理する必要があります - 通常は、項目をポップして項目をポップし、空になるまで処理するループに項目をポップするコードを入れます。このイベントは現在カウントされず、「キューはもはや空ではありません」と通知されます。 (これを行うと、キューを処理している間に、追加されたSetEventが呼び出されたアイテムも処理され、ワー​​カースレッドがWaitForSingleObjectに達するとスレッドが起動しますがアイテムが既に処理されているのでキューが空であることがわかります;これは最初は少し驚くようですが、実際は問題ありません。)

両方に小さな賛否両論がありますが、どちらも正しいです。 (個人的には、「仕事が必要なもの」という概念や、その仕事やデータの量から「より多くのデータが利用できる」という概念を切り離しているので、イベントアプローチが好きです)。

+1

ありがとうございました。あなたの説明は非常にはっきりしています。上に挙げた2つの方法は良い方法です。私の上記のコードでは、WaitForSingleObject()を呼び出す前に空の状態でキューをチェックしたので、正しく動作したと思います。 – TTGroup

+0

私は、メッセージキューが空であることを観察した後でイベントを待つだけなので、書かれたループは、上記の問題を抱えていないことに同意します。それでも、潜在的な落とし穴の説明とそれを回避する方法は良いです。 –

+0

@Vincent Povirk:ありがとうございますが、上記のコードでは "潜在的な落とし穴"はありません。あなたは私にその事を教えてもらえますか?再度、感謝します。 – TTGroup

0

複数のスレッドが同時にデータを消費した場合、またはSetEventの代わりにPulseEventを使用した場合、危険です。

しかし、消費者が1人で、イベントが待つまで(オートレストの場合)、または永遠に(手動リセットの場合)、イベントは通知されますので、動作するはずです。

+0

はい、私はそれを理解しています。ありがとうございました ! – TTGroup

2

「古典的な」方法(つまり、正しく動作します)は、セマフォを使用することです(CreateSemaphore、ReleaseSemaphore APIを参照)。セマフォを空にします。プロデューサスレッドでは、ミューテックスをロックし、メッセージをプッシュし、ミューテックスをロック解除し、ユニットをセマフォにリリースします。消費者スレッドでは、WFSOでセマフォハンドルを待って(上記のイベントを待つように)、mutexをロックし、メッセージをポップし、mutexをロック解除します。

なぜイベントよりも優れていますか?

1)キューカウントを確認する必要はありません。セマフォはメッセージを数えます。

2)スレッドが待機していないため、セマフォへの信号は「失われません」。

3)待ち行列の数をチェックしないと、結果として、そのようなチェックの結果得られたコードパスがプリエンプションのために間違っている可能性があります。

4)複数のプロデューサと複数のコンシューマに対して、変更なしで動作します。

5)それはよりクロスプラットフォームであり、すべてのプリエンプティブOSにはミューテックス/セマフォがあります。

+0

ありがとう、私はこの場合に適用するのが良い方法だと思います。しかし、私は上記のコードをシンプルに保つかもしれません。^ – TTGroup

+0

+1 - (2)はイベントにも適用されますが、 PulseEventではなくSetEventを使用している限り、イベント信号もスレッドが待機していないため失われません。 – BrendanMcK

+0

@BrendanMcK - はい、イベントは失われます。空のセマフォを1回シグナルすると、そのカウントは1になります。空のセマフォを100回シグナルすると、そのカウントは100になります。イベント - 何回信号を送信しても、カウントはありません。 –

関連する問題