Concurrency 使用ConcurrentQueue时,在并行循环时尝试退出队列

Concurrency 使用ConcurrentQueue时,在并行循环时尝试退出队列,concurrency,c#-4.0,plinq,pfx,Concurrency,C# 4.0,Plinq,Pfx,我在.NET4应用程序中使用并行数据结构,我有一个ConcurrentQueue,当我处理它时,它会被添加到其中 我想做一些类似的事情: personqueue.aspallel().WithDegreeOfParallelism(20).ForAll(i=>…) 当我调用数据库来保存数据时,我限制了并发线程的数量 但是,我希望所有的都不会退出队列,我担心的只是这样做 ForAll(i => { personqueue.personqueue.TryDequeue(...);

我在.NET4应用程序中使用并行数据结构,我有一个
ConcurrentQueue
,当我处理它时,它会被添加到其中

我想做一些类似的事情:

personqueue.aspallel().WithDegreeOfParallelism(20).ForAll(i=>…)

当我调用数据库来保存数据时,我限制了并发线程的数量

但是,我希望所有的
都不会退出队列,我担心的只是这样做

ForAll(i => {
    personqueue.personqueue.TryDequeue(...);
    ...
});
因为我不能保证我是正确的

那么,如何以并行方式迭代集合和出列呢


或者,使用
PLINQ
来并行执行此处理会更好吗?

我不能100%确定您试图在这里归档什么。您是否正在尝试将所有项目出列,直到什么都不剩?还是一次就把很多东西排出来

第一个可能意外的行为始于以下语句:

 theQueue.AsParallel()
对于ConcurrentQueue,您将获得一个“快照”-枚举器。因此,当您迭代并发堆栈时,您只迭代快照,而不迭代“活动”队列

总的来说,我认为在迭代过程中对您正在更改的内容进行迭代不是一个好主意

因此,另一个解决方案如下所示:

        // this way it's more clear, that we only deque for theQueue.Count items
        // However after this, the queue is probably not empty
        // or maybe the queue is also empty earlier   
        Parallel.For(0, theQueue.Count,
                     new ParallelOptions() {MaxDegreeOfParallelism = 20},
                     () => { 
                         theQueue.TryDequeue(); //and stuff
                     });
这避免了在迭代过程中对某些内容进行操作。但是,在该语句之后,队列仍然可以包含在for循环期间添加的数据

要及时清空队列,您可能需要多做一些工作。这是一个非常丑陋的解决方案。当队列仍有项目时,创建新任务。每个任务开始时,尽可能长时间地从队列中退出。最后,我们等待所有任务结束。为了限制并行性,我们从不创建超过20个任务

        // Probably a kitty died because of this ugly code ;)
        // However, this code tries to get the queue empty in a very aggressive way
        Action consumeFromQueue = () =>
                                      {
                                          while (tt.TryDequeue())
                                          {
                                              ; // do your stuff
                                          }
                                      };
        var allRunningTasks = new Task[MaxParallism];
        for(int i=0;i<MaxParallism && tt.Count>0;i++)
        {
            allRunningTasks[i] = Task.Factory.StartNew(consumeFromQueue);  
        }
        Task.WaitAll(allRunningTasks);
//可能是一只小猫因为这个丑陋的代码而死;)
//然而,这段代码试图以一种非常激进的方式将队列清空
Action ConsumerFromQueue=()=>
{
while(tt.TryDequeue())
{
做你的事
}
};
var allRunningTasks=新任务[MaxParallism];
for(int i=0;i0;i++)
{
allRunningTasks[i]=Task.Factory.StartNew(ConsumerFromQueue);
}
Task.WaitAll(所有运行任务);

如果您的目标是一个高吞吐量的真实站点,并且不必立即进行数据库更新,那么最好使用非常保守的解决方案,而不是额外的层库

制作固定大小的数组(guestimate size-比如1000个项目或N秒的请求)和互锁索引,以便请求将数据放入插槽并返回。当一个块被填满时(继续检查计数),创建另一个块并生成异步委托进行处理,并将刚刚填满的块发送给SQL。根据您的数据结构,委托可以将所有数据打包到逗号分隔的数组中,甚至可能是一个简单的XML(当然要测试该数组的性能)并将其发送到SQL存储过程,该存储过程应提供最好的逐记录处理方法—永远不要持有大锁。如果它变得很重,你可以把你的积木分成几个较小的积木。关键的一点是,您最小化了对SQL的请求数量,始终保持一个分离度,甚至不必为线程池付出代价——您可能根本不需要使用超过2个异步线程


这将比摆弄并行s快得多。

我将尝试中间的想法,因为这可能会奏效。这是一个web服务的一部分,它可能在很短的时间内得到10k个更新,一次一个,但我不想试图在每次更新时都对数据库进行重击,所以我会将它们推到一个静态队列中,以单例的形式,并让一个函数进行处理。我的解决方案并不完美,但我需要将数据库作为一个高优先级来保护。我的希望是在队列被填满时将其清空,因此如果我在一个web服务调用中得到数百个项目,那么他们会发送另一个大批,当队列中有东西时,它将继续进行。现在我睡10秒钟,处理队列,然后再睡10秒钟。