【发布时间】:2012-03-26 09:00:35
【问题描述】:
我需要构建一个在 WCF 中侦听新任务的进程。 (异步) 每个任务(以某种方式)都会入队。
循环队列和出队的最佳(逻辑和性能)方法是什么。
我想过:
while(true){
queue.Dequeue();
}
我认为有更好的方法来做到这一点。
谢谢
【问题讨论】:
我需要构建一个在 WCF 中侦听新任务的进程。 (异步) 每个任务(以某种方式)都会入队。
循环队列和出队的最佳(逻辑和性能)方法是什么。
我想过:
while(true){
queue.Dequeue();
}
我认为有更好的方法来做到这一点。
谢谢
【问题讨论】:
看看System.Collections.Concurrent 命名空间 - 有线程安全的队列实现,即。 ConcurrentQueue - 不过,我怀疑BlockingCollection 会更好地满足您的需求。
阻塞集合本质上是一个线程安全的集合,适用于生产者-消费者场景。在您的情况下,WCF 调用将充当将添加到集合中的生产者,而工作线程将充当消费者,基本上将从集合中获取排队的任务。通过使用单个消费者(和集合),您可以确保执行顺序。如果这不重要,那么您可以使用多个消费者线程。 (如果需要,还有AddAny 和TakeAny 静态重载允许您使用多个集合(多个队列)。)
与while(true) 方法相比的优势在于避免了只会消耗 CPU 周期的紧密循环。除了线程安全之外,这还可以解决排队和出队线程之间的同步问题。
编辑:
Blocking Collection 使用起来非常简单。请参见下面的简单示例 - 将从您的 WCF 方法调用添加任务以将任务排队,而在服务启动期间将调用 StartConsumer。
public class MyTask { ... }
private BlockingCollection<MyTask> _tasks = new BlockingCollection<MyTask>();
private void AddTask(MyTask task)
{
_tasks.Add(task);
}
private void StartConsumer()
{
// I have used a task API but you can very well launch a new thread instead of task
Task.Factory.StartNew(() =>
{
while (!_tasks.IsCompleted)
{
var task = _tasks.Take();
ProcessTask(task);
}
});
}
在停止服务时,需要调用_tasks.CompleteAdding 以便消费者线程中断。
在 MSDN 上查找更多示例:
http://msdn.microsoft.com/en-us/library/dd997306.aspx
http://msdn.microsoft.com/en-us/library/dd460690.aspx
http://msdn.microsoft.com/en-us/library/dd460684.aspx
【讨论】:
"while StartConsumer will be called during service start-up"如何调用StartConsumer,我应该放在哪里?
AddTask、StartConsumer 等方法公开静态。
我会使用事件来同步队列,而不是无限循环。每当进行 WCF 调用时,将一个元素添加到队列并发送一个“AnElementHasBeenAddedEvent”。
执行队列任务的线程会监听该事件,每当它接收到该事件时,队列就会被清空。
确保只有一个线程可以完成这项工作!
while(true) 概念的优势:您没有一个线程不断循环通过无限循环并因此吃掉资源。你只做需要的工作。
【讨论】:
Synchronized方法作为队列不是线程安全的:Queue.Synchronized(queue).Dequeue()