如何创建自定义消息泵?(How to create custom message pump?)

2019-08-17 02:14发布

我做一个小的锻炼,我需要建立一个类似于一个消息泵。 我有什么是工作队列做,我想工作,完全在一个线程中完成,而任何线程可以添加工作队列工作要做。

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的?

Answer 1:

您可以使用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();
    }
}


Answer 2:

你不应该做一个完整的互斥体,但你可以把一个锁声明

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

无论你想要锁定的对象使用,大多数人会说,使用信号本身是一个坏主意。

应对@ 500 - 下面服务器内部错误的评论,你可以只在信号复位做的工作之前。 下面应该保护的东西:

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

  signal.WaitOne();
}

这样一来,如果你有更多的工作,内部队列中不断去。 如果不是,它掉下来的signal.WaitOne()我们只如果没有已经排队更多的工作复位。

这里唯一的缺点是,它是可能的,我们将在一排,如果下班,而在多次重置DoWork正在执行。



Answer 3:

您可以使用的WaitOne(时间跨度) ,让你有一个混合信号/轮询循环方法。 基本上指定最多等待1秒的时间跨度。 这将导致任务陷入在那场比赛举行至多第二(或任何轮询时间指定),或者直到另一个任务添加到您的队列。



文章来源: How to create custom message pump?