我做一个小的锻炼,我需要建立一个类似于一个消息泵。 我有什么是工作队列做,我想工作,完全在一个线程中完成,而任何线程可以添加工作队列工作要做。
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();
}
}
你不应该做一个完整的互斥体,但你可以把一个锁声明
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
正在执行。
您可以使用的WaitOne(时间跨度) ,让你有一个混合信号/轮询循环方法。 基本上指定最多等待1秒的时间跨度。 这将导致任务陷入在那场比赛举行至多第二(或任何轮询时间指定),或者直到另一个任务添加到您的队列。