我在我的.NET 4应用程序中使用并行数据结构,并且在我处理它时会添加ConcurrentQueue
。
我想做类似的事情:
personqueue.AsParallel().WithDegreeOfParallelism(20).ForAll(i => ... );
当我进行数据库调用以保存数据时,所以我限制了并发线程的数量。
但是,我希望ForAll
不会出列,而我只关心做什么
ForAll(i => {
personqueue.personqueue.TryDequeue(...);
...
});
因为无法保证我正在弹出正确的。
那么,我怎样才能以并行的方式遍历集合并出列队列。
或者,并行使用PLINQ
进行此处理会更好吗?
答案 0 :(得分:4)
嗯,我不是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);
答案 1 :(得分:0)
如果你的目标是整个真实网站的高端,你不必立即进行数据库更新,你会更好地选择非常保守的解决方案而不是额外的图层库。
制作固定大小的数组(猜测大小 - 比如1000项或N秒的请求)和互锁索引,以便请求只将数据放入插槽并返回。当一个块被填满(继续检查计数)时,再创建一个并生成异步委托来处理并向SQL发送刚填充的块。根据数据的结构,委托可以将所有数据打包成逗号分隔的数组,甚至可能是一个简单的XML(当然要测试那个数据的性能)并将它们发送到SQL sproc,这应该给它最好处理它们的记录记录 - 从不持有大锁。如果变重,您可以将块分成几个较小的块。关键是你最小化了对SQL的请求数量,始终保持一定程度的分离,甚至不必为线程池付出代价 - 你可能根本不需要使用更多的2个异步线程
摆弄Parallel-s会快得多。