2009-04-16 14 views
4

これにアプローチする方法は本当にわかりませんが、カスタムクラス内で発生したイベントを購読していて、最初にキューに入れて最初に処理したいのが理想です彼らが入ってくると、私はQueue<T>を知っています。私はこれを使うべきだと思いますか?私の質問は、イベントハンドラのメッセージが受信されたときに、私は単純にEnqueue()をキューに入れるでしょうか?もしそうなら、新しいアイテムが追加されたときにキューはどのようにして突き抜けられますか?新しい要素を受け取ったときにスレッド内のループスルーリスト

私は(あなたの帽子の上ホールド)のようなものを実行し、コンストラクタでメソッドを呼び出して検討していた:

while (true) 
{ 
    foreach (var item in myQueue) 
    { 
    // process 
    myQueue.Dequeue(); 
    } 
} 

確かにこれを行うには、よりエレガントな方法がなければなりませんか?これは効果的にmyQueueをヒットし、要素が含まれているので反復し、私がしたいことをします。パフォーマンスはどのようなものでしょうか?スレッドをブロックするのを避けるために別のスレッドでこれを生成することができます。私は実際にはwhile (true)を受け入れる時間がありました!

答えて

2

あなたが複数のスレッドからこれを実行している場合、あなたはいくつかのフォームを導入する必要がありますあなたのキューの同期の問題を防ぐためにロックを解除します。また、要素のエンキューや要素のデキュー時に、キューがロックされていることを確認する必要があります。

キューを処理しているスレッドがこれ以外何もしない場合は、キューが現在空の場合を処理する以外は基本コードが大丈夫でしょう。

while (myQueue.Count == 0) 
    Thread.Sleep(sleepTime); 

このようにすると、イベントがキューに入るまで「待機」することができます。

また、キューからデキューすると、foreachを使用できなくなります。これは、誰かがあなたのキューに追加した場合、コレクションの変更の問題を回避し、全体の時間あなたがあなたの要素の処理をロックする必要からあなたを維持します

while (myQueue.Count == 0) 
    Thread.Sleep(sleepTime); 
while (myQueue.Count > 0) 
{ 
    lock(myLock) 
     myObject = myQueue.Dequeue(); 
    // do your work... 
} 

:あなたのような何かをしたいと思います。


編集:これは常に最も効果的な戦略ではないという意見に同意します。

キューが大部分空で、場合によっては要素しか持たない場合は、それをラップするカスタムクラスを作成します。

キューが空/非空になったときにイベントが発生する可能性があります。待ち行列がアイテムを受け取るとすぐに、イベントはスレッドがそれを処理してそれを空にするようトリガーする可能性があります。それが0のアイテムに到達するとすぐに、処理を中止するイベントを持つことができます。

これは、キューの状態がほとんど空の場合には、常に待機するよりもはるかに効果的です。一方、キューがほぼ常に満杯で、処理スレッドがめったに追いつかない場合は、上記のアプローチを単純化のために使用します。

+0

良い提案。ありがとうリード。私はそれにいくつかの考えを与えるだろう、はい、私のスレッドは、これを何もすることはできませんでした、私はそれに満足しています。 – GONeale

+0

しないでください。忙しい待ち時間は、性能とバッテリ寿命に悪いです。キューにいくつかの項目があるのを待つ必要がある場合は、何らかの種類のイベントを使用する必要があります。 –

+0

これは私が1800をやりたいことです。キューのクラス、つまりイベントハンドラで最初にチェックしたことですなし。私はキューから継承し、エンキューコマンドをオーバーロードすることで自分自身を作ることができると思いますか?どう思いますか? – GONeale

4

これは古典的な生産者/消費者の問題です。クイックウェブ検索では、これを正確にカバーするhttp://msdn.microsoft.com/en-us/library/yy12yx1f(VS.80,loband).aspxが明らかになりました。

作業がない場合でも、スレッドがCPUの100%を占有し、システム内の他のスレッドを枯渇させる可能性があるため、while(true)ループを実行する必要はありません。

0

あなたがしていることは正しく見えません。あなたが本当にキューを使用している場合、あなたは本当にそれを反復いない、キューの外にアイテムを引っ張っする必要があります。

while (!queue.empty()) // or whatever 
{ 
    process the first item in the queue 
} 
+0

@ 1800、Reedの投稿にあなたのコメントがあります。 – GONeale

+0

@ 1800の場合は、queue.Empty()とqueue.Count()が一致しない場合はwhileを終了します。 – GONeale

4

これはできません。 enumeratorがまだ使用されている間、基になるコレクションが変更された場合、foreachに対して返された列挙子は例外をスローします。

基本的には、イベントを処理する別のスレッドを設定する必要があります。理想的には、イベントやその他の同期メカニズムを介してこの他のスレッドに利用可能な作業があることを通知します。作業項目をデキューして処理し、次のシグナルが来るまでスリープします。あるいは、ポーリングを定期的に起動して別の項目をチェックすることもできますが、効率が悪くなります。いずれの場合でも、ロックを使用して、両方のスレッドのキューを同時に変更しないようにする必要があります。

1

私は通常、要素がコレクションに追加されたことを通知するためにManualResetEventを使用します。

イベントハンドラは、このような何かを行います。

lock (myLock) 
{ 
    myq.Enqueue(...); 
    myqAddedSignal.Set(); 
} 

信号の処理スレッドが待機 - 一度アイテムを処理し、信号をリセットし、それはキューを空に合図:

while (true) 
{ 
    myqAddedSignal.WaitOne(); 
    lock (myLock) 
    { 
     // pull everything off myQ into a list 
     // clear myQ 
     myqAddedSignal.Reset(); 
    } 

    foreach (obj in copyOfMyQ) 
    { 
     ... 
    } 
} 

これをスレッドセーフな方法でキュー内の項目を処理します。唯一の共有状態はmyqAddedSignalです - myLockでのアクセスは同期しています(通常はこれをオブジェクトにします)。

0

Reedから与えられたアドバイスに続いて、私はカスタムクラスを作成し、キューが空になっていったときにイベントをスローしました。

カスタムEventQueue<T>クラス:

public class EventQueue<T> : Queue<T> 
{ 
    public delegate void OnQueueMadeEmptyDelegate(); 
    public event OnQueueMadeEmptyDelegate OnQueueMadeEmpty; 
    public delegate void OnQueueMadeNonEmptyDelegate(); 
    public event OnQueueMadeNonEmptyDelegate OnQueueMadeNonEmpty; 

    public new void Enqueue(T item) 
    { 
     var oldCount = Count; 
     base.Enqueue(item); 
     if (OnQueueMadeNonEmpty != null && 
      oldCount == 0 && Count > 0) 
      // FIRE EVENT 
      OnQueueMadeNonEmpty(); 
    } 
    public new T Dequeue() 
    { 
     var oldCount = Count; 
     var item = base.Dequeue(); 
     if (OnQueueMadeEmpty != null && 
      oldCount > 0 && Count == 0) 
     { 
      // FIRE EVENT 
      OnQueueMadeEmpty(); 
     } 
     return item; 
    } 
    public new void Clear() 
    { 
     base.Clear(); 
     if (OnQueueMadeEmpty != null) 
     { 
      // FIRE EVENT 
      OnQueueMadeEmpty(); 
     } 
    } 
} 

(私は小さなコードサンプルのため<概要>さんを削除した私は、基本ロジックに追加ロジックを追加する方法として、 『新しい』修飾子を使用しています。)。メインクラスで

のプライベートショー:メインクラスのコンストラクタで

public delegate void InitQueueDelegate(); 
private InitQueueDelegate initQueueDelegate; 

private EventQueue<QueueRequest> translationQueue; 
private Object queueLock = new Object(); 

private void InitQueue() 
{ 
    this.translationQueue = new EventQueue<QueueRequest>(); 
    this.translationQueue.OnQueueMadeEmpty += new EventQueue<QueueRequest>.OnQueueMadeEmptyDelegate(translationQueue_OnQueueMadeEmpty); 
    this.translationQueue.OnQueueMadeNonEmpty += new EventQueue<QueueRequest>.OnQueueMadeNonEmptyDelegate(translationQueue_OnQueueMadeNonEmpty); 
} 

void translationQueue_OnQueueMadeNonEmpty() 
{ 
    while (translationQueue.Count() > 0) 
    { 
     lock (queueLock) 
     { 
      QueueRequest request = translationQueue.Dequeue(); 
#if DEBUG 
      System.Diagnostics.Debug.WriteLine("Item taken from queue..."); 
#endif 
      // hard work 
      .... 
      .... 
      .... 
     } 
    } 
} 

void translationQueue_OnQueueMadeEmpty() 
{ 
    // empty queue 
    // don't actually need to do anything here? 
} 

private void onMessageReceived(....) 
{ 
    .... 
    .... 
    .... 
    // QUEUE REQUEST 
    lock (queueLock) 
    { 
    QueueRequest queueRequest = new QueueRequest 
            { 
             Request = request, 
             Sender = sender, 
             Recipient = tcpClientService 
            }; 
    translationQueue.Enqueue(queueRequest); 
#if DEBUG 
    System.Diagnostics.Debug.WriteLine("Item added to queue..."); 
#endif 
    } 
} 

そして最後にQueueRequest構造体:

public struct QueueRequest 
{ 
    public MessageTranslateRequest Request { get; set; } 
    public TCPClientService Sender { get; set; } 
    public TCPClientService Recipient { get; set; } 
} 
メインクラス本体で

initQueueDelegate = this.InitQueue; 
initQueueDelegate.BeginInvoke(null, null); 

私はそこにたくさんのことがあることを知っていますが、皆さんが完全な実装をチェックすることを望みました。どう思いますか?どのように私は正しいロックを実行しましたか?

解決策が彼のアイデアから作成されたため、これが問題なければ、私はReedに賞を授与します。

0

探している構造はセマフォです。

アイテムをキューに追加し、セマフォにカウントを追加します。

他のスレッドのセマフォを待ち、キューから項目を処理します。

キューインプリメンテーション(BCLのような)によっては、取得中にロック/アンロックする必要があります。

関連する問題