我的程序中使用了Parallel.ForEach()
语句。它使用一些对象的列表作为输入。我不关心输出顺序,但我需要这个循环以输入元素的顺序输入元素。是否可以使用Parallel.ForEach()
实现此目的?
答案 0 :(得分:3)
如果您需要保留IEnumerable<T>
的订单,则可能需要implement a custom partitioner种类OrderablePartitioner<T>
。这个类的示例代码包含一个简单的示例,它以递增的顺序从枚举中一次检索一个。
然而,对于像ConcurrentQueue<T>
这样的简单的生产者 - 消费者模型来说,这是很多工作:
var queue = new ConcurrentQueue<X>(yourEnumerableOfX);
Action consumer = () =>
{
X x;
while (queue.TryDequeue(out x))
{
x.Frob();
}
};
// At most N "in flight"
int maxParallelism = Environment.ProcessorCount;
var consumers = Enumerable.Repeat(consumer, maxParallelism).ToArray();
Parallel.Invoke(consumers);
使用此代码,您将获得先进先出行为,并且您的请求最终会按照收到的顺序“在飞行中”处理。一旦并行放置,您将无法保证它们按顺序排列。
或者,您可以使用以下内容(限制队列项的数量保持不变):
// Executes exactly queue.Count iterations at the time of Parallel.ForEach
// due to "snapshot" isolation of ConcurrentQueue<X>.GetEnumerator()
var queue = new ConcurrentQueue<X>(yourEnumerableOfX);
Parallel.ForEach(
queue,
_ =>
{
X x;
if (queue.TryDequeue(out x))
{
x.Frob();
}
});
如果您希望继续在一个线程中生成,而在其他线程中使用,则使用带有队列的BlockingCollection<T>
作为其后备集合:
var queue = new BlockingCollection<X>(new ConcurrentQueue<X>());
// add to it
Task.Factory.StartNew( () =>
{
foreach (var x in yourEnumerableOfX)
{
queue.Add(x);
Thread.Sleep(200);
}
// Signal to our consumers we're done:
queue.CompleteAdding();
});
现在我们需要“无限制”的消费者,因为我们不确定可能存在多少个队列项目:
// Roughly the same consumer code as above, but 'unbounded'
Action consumer = () =>
{
while (!queue.IsCompleted)
{
X x;
try
{
// blocking form, switch to TryTake and maybe Thread.Sleep()
x = queue.Take();
}
catch (InvalidOperationException)
{
// none left
break;
}
x.Frob();
}
};
int maxParallelism = Environment.ProcessorCount;
var consumers = Enumerable.Repeat(consumer, maxParallelism).ToArray();
Parallel.Invoke(consumers);
答案 1 :(得分:2)
此功能不存在,因为OS调度程序可以暂停项目10的执行并将项目11放在CPU核心上,以便11在10之前执行。没有库可以抵消它。任何工作项都可以无限期暂停。
您可以获得近似排序。请参阅其他答案。
答案 2 :(得分:0)
不,根据文档所有并行迭代器(for和foreach)并不保证任何订单处理项目,它始终是不可预测的
第二个paraghraph msdn documentation of parallel loops