我正在做一个小练习,需要创建类似于消息泵的内容。我所要做的工作是排队,我希望工作完全在一个线程上完成,而任何线程都可以将工作添加到要完成的队列中。

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周围的情况下缓解这种情况?

最佳答案

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

10-05 19:06