我有一个相当普通的网络服务(旧学校asmx)。其中一种方法启动了一些与返回给客户端的结果无关的异步处理。希望下面的小片段有意义:
[System.Web.Services.WebMethod]
public List<Foo> SampleWebMethod(string id)
{
// sample db query
var foo = db.Query<Foo>("WHERE id=@0",id);
// kick of async stuff here - for example firing off emails
// dont wait to send result
DoAsyncStuffHere();
return foo;
}
我对DoAsyncStuffHere方法的初始实现使用了ThreadPool.QueueUserWorkItem。所以,它看起来像:
public void DoAsyncStuffHere()
{
ThreadPool.QueueUserWorkItem(delegate
{
// DO WORK HERE
});
}
这种方法在低负载条件下工作正常。但是,我需要能够处理相当高负载的东西。因此,生产者/消费者模式似乎是最好的方式。
我感到困惑的是如何将队列所做的所有工作限制在Web服务的所有实例 的 单线程中。我如何才能最好地设置单个队列以供任何Web服务实例访问?
答案 0 :(得分:5)
您可以使用System.Collections.Concurrent.BlockingCollection<T>
作为基础集合System.Collections.Concurrent.ConcurrentQueue<T>
正如名称空间的名称所暗示的那样,集合是线程安全的。
使用Take()
方法启动一个消费者线程(或几个)从集合中提取项目。当没有可用的项目时,线程将阻止。
您的DoAsyncStuffHere方法将项添加到BlockingCollection。这些项目可能是未启动的System.Threading.Tasks.Task
个对象;在这种情况下,消费者线程将Start
从集合中获取后的任务。
答案 1 :(得分:1)
一种简单的方法是将队列实现为数据库表。
生产者将是每个Web服务实例处理的请求线程。
消费者可以是任何类型的持续运行的进程(Windows窗体应用程序,Windows服务,数据库作业等),它们监视队列并一次处理一个项目。
答案 2 :(得分:1)
你不能用ThreadPool
做到这一点 - 你可以有一个静态构造函数来启动一个工作线程; DoAsyncStuffHere
可以将其工作项插入到您想要完成的工作队列中,并且工作线程可以检查队列中是否有任何项目可以处理。如果是这样,那就完成了工作,否则就会睡几毫秒。
静态构造函数确保它只被调用一次,并且只应启动一个Thread(除非有一些我不知道的奇怪的.NET边缘情况)。
这是一个示例的布局 - 您可能需要在队列上实现一些锁定并为工作线程添加更多复杂性,但我在成功之前使用了此模式。 WorkItem
对象可以保存您希望传递给工作线程的信息的状态。
public static WebService()
{
new Thread(WorkerThread).Start();
WorkQueue = new Queue<WorkItem>();
}
public static void WorkerThread()
{
while(true)
{
if(WorkQueue.Any())
{
WorkQueue.Dequeue().DoWork();
}
else
{
Thread.Sleep(100);
}
}
}
public static Queue<WorkItem> WorkQueue { get; set; }
[System.Web.Services.WebMethod]
public List<Foo> SampleWebMethod(string id)
{
WorkQueue.Queue(newWorkItem());
}