在C#中同步繁重和轻量级任务

时间:2014-01-24 19:09:12

标签: c# multithreading concurrency task

我正在尝试使用不同的任务权重来实现某种任务队列,允许根据权重同时运行不同数量的任务。

有两种类型的任务:长任务和短任务。 最多可以同时执行N个短任务。

当长任务出现时,如果没有其他长任务正在运行,它应立即启动,否则等待它完成。

如果有长任务运行,则应将并发短任务计数限制减少到M. 已经运行的短任务应该继续运行完成;但如果当前限制小于或等于当前正在运行的短任务数量,则不应启动新的短任务。

看起来,我基本上需要动态改变信号量“容量”的能力。 通过在需要时获取/释放(N-M)“时隙”来减少/增加容量将是容易的,但是如果存在N短,则在(N-M)短任务完成之前将导致队列“挂起”任务已在运行。

我还可以实现某种“调度程序”每100毫秒唤醒一次(例如)并检查队列中是否有任何可以立即启动的任务。这种方法的缺点是在排队任务和启动任务之间有最多100毫秒的延迟。

所以我对这个难题感到困惑,并希望有人能够对如何实现这一点有一些新的想法。

更新的 任务不会产生任何重大的CPU负载。 它们实际上是HTTP请求。长请求是上传文件,短请求是常见的HTTP请求。

1 个答案:

答案 0 :(得分:2)

我回答了一个非常相似的问题a few days ago,您的解决方案几乎完全相同,请使用QueuedTaskScheduler  来自“ParallelExtensionsExtras

private static void Main(string[] args)
{
    int highPriorityMaxConcurrancy = 1

    QueuedTaskScheduler qts = new QueuedTaskScheduler();
    var highPriortiyScheduler = qts.ActivateNewQueue(0);
    var lowPriorityScheduler = qts.ActivateNewQueue(1);

    BlockingCollection<HttpRequestWrapper> fileRequest= new BlockingCollection<Foo>();
    BlockingCollection<HttpRequestWrapper> commonRequest= new BlockingCollection<Foo>();

    List<Task> processors = new List<Task>(2);

    processors.Add(Task.Factory.StartNew(() =>
    {
        Parallel.ForEach(fileRequest.GetConsumingPartitioner(),  //.GetConsumingPartitioner() is also from ParallelExtensionExtras, it gives better performance than .GetConsumingEnumerable() with Parallel.ForEeach(
                         new ParallelOptions() { TaskScheduler = highPriortiyScheduler, MaxDegreeOfParallelism = highPriorityMaxConcurrancy }, 
                         ProcessWork);
    }, TaskCreationOptions.LongRunning));

    processors.Add(Task.Factory.StartNew(() =>
    {
        Parallel.ForEach(commonRequest.GetConsumingPartitioner(), 
                         new ParallelOptions() { TaskScheduler = lowPriorityScheduler}, 
                         ProcessWork);
    }, TaskCreationOptions.LongRunning));


    //Add some work to do here to the fileRequest or commonRequest collections


    //Lets the blocking collections know we are no-longer going to be adding new items so it will break out of the `ForEach` once it has finished the pending work.
    fileRequest.CompleteAdding();
    commonRequest.CompleteAdding();

    //Waits for the two collections to compleatly empty before continueing
    Task.WaitAll(processors.ToArray());
}

private static void ProcessWork(HttpRequestWrapper request)
{
    //...
}