4

これにどのようにアプローチするかは本当にわかりませんが、カスタムクラス内で発生したイベントをサブスクライブしています.理想的には、それらをキューに入れ、入ってきたときに先入れ先出しで処理したいと考えていますQueue<T>.これを使って?しかし、私の質問は、メッセージが受信されたときのイベント ハンドラーにEnqueue()あります。そこにあるキューに単純に移動しますか?もしそうなら、新しいアイテムが追加されたときにキューをどのようにクランチすることができますか?

コンストラクターで次のようなメソッドを呼び出すことを検討していました (帽子を握ってください):

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

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

4

8 に答える 8

4

これは典型的な生産者/消費者問題です。簡単なWeb検索により、http://msdn.microsoft.com/en-us/library/yy12yx1f( VS.80、loband).aspxが明らかになり、これが正確にカバーされます。

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

于 2009-04-16T01:13:56.207 に答える
4

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

基本的に、あなたがする必要があるのは、イベントを処理するために別のスレッドを設定することです。理想的には、この他のスレッドに(イベントまたは他の同期メカニズムを介して)利用可能な作業があることを通知します。作業項目をデキューして処理し、次の信号が着信するまでスリープします。または、ポーリングを使用することもできます(定期的にウェイクアップして別の項目を確認します)が、効率は低下します。いずれの場合も、両方のスレッドのキューを同時に変更しようとしないように、ロックを使用する必要があります。

于 2009-04-16T01:15:38.637 に答える
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に達するとすぐに、処理を停止するイベントが発生する可能性があります。

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

于 2009-04-16T01:14:55.127 に答える
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 で同期されます (通常、これをオブジェクトにします)。

于 2009-04-16T01:35:54.220 に答える
0

あなたがそこでしていることは正しく見えません。実際にキューを使用している場合は、アイテムを繰り返し処理するのではなく、実際にキューからアイテムをプルする必要があります。

while (!queue.empty()) // or whatever
{
  process the first item in the queue
}
于 2009-04-16T01:14:40.043 に答える
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();
        }
    }
}

(コード サンプルを小さくするために <summary> を削除しました。基本ロジックにロジックを追加する方法として、"new" 修飾子を使用しています)。

メインクラスのプライベート:

public delegate void InitQueueDelegate();
private InitQueueDelegate initQueueDelegate;

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

メインクラスのコンストラクター:

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

メインクラス本体:

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

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

私のソリューションは彼のアイデアから作成されたので、これでよければリードにクレジットを与えます。

于 2009-04-16T04:08:35.663 に答える
0

このようなキューを持つこの既存の回答を見ることができます。

于 2009-04-16T03:45:22.940 に答える
0

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

キューに項目を追加してから、セマフォにカウントを追加します。

他のスレッドでセマフォを待機し、キューからアイテムを処理します。

キューの実装 (BCL など) によっては、取得中にロック/ロック解除する必要があります。

于 2009-04-16T05:28:45.547 に答える