4

メッセージポンプに似たものを作成する必要がある小さな演習を行っています。私が持っているのは、実行する作業のキューです。すべてのスレッドが実行するキューに作業を追加できる一方で、作業を完全に 1 つのスレッドで実行したいと考えています。

Queue<WorkToDo> queue;

スレッドは待機ハンドルを使用して、実行する作業があることをポンプに伝えます。

WaitHandle signal;

ポンプは、やるべき仕事がある限りループし、信号が再開するのを待ちます。

while(ApplicationIsRunning){
    while(queue.HasWork){
        DoWork(queue.NextWorkItem)
    }
    signal.Reset();
    signal.WaitOne();
}

他のすべてのスレッドは、キューに作業を追加し、待機ハンドルを通知できます...

public void AddWork(WorkToDo work){
    queue.Add(work);
    signal.Set();
} 

問題は、作業が十分に速く追加されている場合、作業のキュー チェックと WaitHandle のリセットの間に、別のスレッドが作業をキューに追加できるため、作業がキューに残される状況が発生する可能性があることです。

WaitHandle の周りにコストのかかるミューテックスを配置せずに、その状況を緩和するにはどうすればよいでしょうか?

4

3 に答える 3

3

を使用BlockingCollection<T>して、同期を処理するため、キューの実装をより簡単にすることができます。

public class MessagePump
{
    private BlockingCollection<Action> actions = new BlockingCollection<Action>();

    public void Run() //you may want to restrict this so that only one caller from one thread is running messages
    {
        foreach (var action in actions.GetConsumingEnumerable())
            action();
    }

    public void AddWork(Action action)
    {
        actions.Add(action);
    }

    public void Stop()
    {
        actions.CompleteAdding();
    }
}
于 2013-02-19T20:54:30.373 に答える
2

完全なミューテックスを実行する必要はありませんが、lock ステートメントを配置できますか

public void AddWork(WorkToDo work)
{
  queue.Add(work);
  lock(lockingObject)
  {
    signal.Set();
  }
} 

ロックオブジェクトに必要なものを何でも使用してください。ほとんどの人は、シグナル自体を使用するのは悪い考えだと言うでしょう。

以下の @ 500 - Server Internal Error のコメントに応答すると、作業を行う前に信号をリセットすることができます。以下は物事を保護する必要があります。

while(ApplicationIsRunning)
{
  while(queue.HasWork)
  {
    WorkItem wi;
    lock(lockingObject)
    {
       wi = queue.NextWorkItem;
       if(!queue.HasWork)
       {
          signal.Reset();
       }
    }
    DoWork(wi)
  }

  signal.WaitOne();
}

このようにして、さらに作業がある場合でも、内部キューは継続します。そうでない場合は に分類され、signal.WaitOne()まだキューに入れられている作業が他にない場合にのみリセットされます。

ここでの唯一の欠点は、 のDoWork実行中に作業が入ると、連続して複数回リセットされる可能性があることです。

于 2013-02-19T20:10:41.377 に答える
0

WaitOne(TimeSpan)メソッドを使用して、ハイブリッド シグナル/ポーリング ループを作成できます。基本的に最大1秒の待ち時間を指定します。これにより、タスクがその競合に巻き込まれ、最大で 1 秒間 (または指定したポーリング時間)、または別のタスクがキューに追加されるまで保持されます。

于 2013-02-19T20:09:16.167 に答える