Windows服务和特定数量的异步任务

时间:2016-10-07 19:12:52

标签: c# windows-services async-await

对于项目,我们需要编写一个Windows服务代码,该服务将处理作业队列并执行必要的处理并调用不同的API,并且它将在独立的机器上运行。

上下文:此服务将每x(例如:5)秒轮询SQL数据库,根据优先级和创建日期获取前Y个作业,然后开始处理它们。我们期望这个Windows服务的容量很大,所以我们想让它成为多线程或异步的。如果达到最大并行处理量,我们不希望它启动更多线程。但与此同时,如果我们有7个工作需要30秒,一个工作需要5分钟,我们不希望它等待5分钟的工作完成,然后再循环并开始另一批8个工作。

我们寻找的第一个选项是BackgroundWorker。计时器的每次迭代都将检查每个BackgroundWorker的状态,如果可用,将指示它处理新作业。但是对于.Net框架的较新版本,使用异步方法已经过时了。

我们寻找的第二个选项是Parallel.ForEach和awaitAll。但问题是,如果8个线程中的7个占用1分钟但最后一个占用6分钟,我们不想等到最后一个完成7个新的作业流程。

最合适的方法是完成任务。

我的问题是:有没有办法跟踪多个任务的运行状态?并限制同时运行的任务数量?我是否应该在服务的OnStart()和我的计时器的每个OnElapsed中实现我的所有任务,检查每个任务的状态,如果可用,再次使用新工作启动它?或者我对任务的工作方式完全错误了?

允许的并行处理数量将在app配置文件中定义,并在Windows服务的OnStart中初始化。

1 个答案:

答案 0 :(得分:0)

一种可能的方法是使用TPL Dataflow库。您可以配置块(在这种情况下为ActionBlock)来处理处理并异步地将项目发送到块中(或者如果您愿意,可以同步)。您可以限制要允许的最大并行度,并设置一个容量来限制缓冲区中的项目数。

通过使用SendAsync,您可以允许您的进程异步等待,直到缓冲区中有新项目的空间。使用TPL Dataflow库这非常简单:

var processBlock = new ActionBlock<DataItem>(
    item => ProcessItemAsync(item),
    new ExecutionDataflowBlockOptions
    {
        BoundedCapacity = 10,   // your configured limit
        MaxDegreeOfParallelism = 2 // your configured max
    });

要使用您的块,您可以执行以下操作:

using (var connection = new SqlConnection(...))
{
    while (await reader.ReadAsync().ConfigureAwait(false))
    {
        // offer a message to the processing block, but allow postponement
        // in case we've hit capacity
       await processBlock.SendAsync(item);
    }
}