3

我有一个相当普通的网络服务(老派 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 服务实例访问?

4

3 回答 3

5

您可以使用 aSystem.Collections.Concurrent.BlockingCollection<T>和 aSystem.Collections.Concurrent.ConcurrentQueue<T>作为基础集合。
正如命名空间的名称所暗示的,集合是线程安全的。

使用该方法启动一个消费者线程(或几个)以从集合中提取项目Take()。当没有可用的项目时,线程将阻塞。

您的 DoAsyncStuffHere 方法将项目添加到 BlockingCollection。这些项目可能是未启动的System.Threading.Tasks.Task对象;在这种情况下,消费者线程将在Start从集合中获取任务之后执行任务。

于 2012-05-11T14:27:15.353 回答
1

一种简单的方法是将队列实现为数据库表。

生产者将是由 Web 服务的每个实例处理的请求线程。

消费者可以是任何一种持续运行的进程(Windows 窗体应用程序、Windows 服务、数据库作业等),它一次监视队列并处理一个项目。

于 2012-05-11T14:19:36.580 回答
1

你不能这样做ThreadPool——你可以有一个静态构造函数来启动一个工作线程;可以将其工作项插入到DoAsyncStuffHere您想要完成的工作队列中,并且工作线程可以检查队列中是否有任何项目可以处理。如果是这样,它会完成工作,否则它会休眠几毫秒。

静态构造函数确保它只被调用一次,并且只应该启动一个线程(除非有一些我不知道的奇怪的 .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());
}
于 2012-05-11T14:21:13.880 回答