C# 为什么Parallel.Foreach会创建无尽的线程?
下面的代码继续创建线程,即使队列为空。。直到最终发生OutOfMemory异常。如果将Parallel.ForEach替换为常规ForEach,则不会发生这种情况。有人知道这可能发生的原因吗C# 为什么Parallel.Foreach会创建无尽的线程?,c#,.net,multithreading,task-parallel-library,C#,.net,Multithreading,Task Parallel Library,下面的代码继续创建线程,即使队列为空。。直到最终发生OutOfMemory异常。如果将Parallel.ForEach替换为常规ForEach,则不会发生这种情况。有人知道这可能发生的原因吗 public delegate void DataChangedDelegate(DataItem obj); public class Consumer { public DataChangedDelegate OnCustomerChanged; public DataChangedD
public delegate void DataChangedDelegate(DataItem obj);
public class Consumer
{
public DataChangedDelegate OnCustomerChanged;
public DataChangedDelegate OnOrdersChanged;
private CancellationTokenSource cts;
private CancellationToken ct;
private BlockingCollection<DataItem> queue;
public Consumer(BlockingCollection<DataItem> queue) {
this.queue = queue;
Start();
}
private void Start() {
cts = new CancellationTokenSource();
ct = cts.Token;
Task.Factory.StartNew(() => DoWork(), ct);
}
private void DoWork() {
Parallel.ForEach(queue.GetConsumingPartitioner(), item => {
if (item.DataType == DataTypes.Customer) {
OnCustomerChanged(item);
} else if(item.DataType == DataTypes.Order) {
OnOrdersChanged(item);
}
});
}
}
public委托无效datachangedelegate(DataItem obj);
公共类消费者
{
公共数据更改客户更改后的Legate;
公共数据更改Legate OnOrdersChanged;
私有取消令牌源cts;
私有取消令牌ct;
私有阻塞收集队列;
公共消费者(阻止收集队列){
this.queue=队列;
Start();
}
私有void Start(){
cts=新的CancellationTokenSource();
ct=cts.Token;
Task.Factory.StartNew(()=>DoWork(),ct);
}
私房{
Parallel.ForEach(queue.GetConsumingPartitioner(),item=>{
if(item.DataType==DataTypes.Customer){
一旦客户变更(项目);
}else if(item.DataType==DataTypes.Order){
订单变更(项目);
}
});
}
}
我认为Parallel.ForEach()
主要用于处理有界集合。而且它不希望像GetConsumingPartitioner()
返回的集合那样,其中MoveNext()
会长时间阻塞
问题在于Parallel.ForEach()
试图找到最佳的并行度,因此只要TaskScheduler
允许它运行,它就会启动尽可能多的Task
s。但是TaskScheduler
发现有许多任务需要很长时间才能完成,而且它们什么都没有做(它们会阻塞),因此它会继续启动新的任务
我认为最好的解决方案是设置MaxDegreeOfParallelism
作为替代方案,您可以使用TPL数据流的ActionBlock
。这种情况的主要区别在于,ActionBlock
在没有要处理的项目时不会阻止任何线程,因此线程数量不会接近极限。我认为Parallel.ForEach()
主要用于处理有界集合。而且它不希望像GetConsumingPartitioner()
返回的集合那样,其中MoveNext()
会长时间阻塞
问题在于Parallel.ForEach()
试图找到最佳的并行度,因此只要TaskScheduler
允许它运行,它就会启动尽可能多的Task
s。但是TaskScheduler
发现有许多任务需要很长时间才能完成,而且它们什么都没有做(它们会阻塞),因此它会继续启动新的任务
我认为最好的解决方案是设置MaxDegreeOfParallelism
作为替代方案,您可以使用TPL数据流的ActionBlock
。这种情况的主要区别在于,ActionBlock
在没有要处理的项目时不会阻止任何线程,因此线程数量不会接近限制。在任务并行库内部,Parallel.For和Parallel.Foreach遵循爬山算法来确定操作应使用多少并行性
或多或少,他们从一个任务开始运行身体,然后移动到两个任务,依此类推,直到达到一个断点,他们需要减少任务数量
这对于快速完成的方法体非常有效,但是如果方法体需要很长时间才能运行,则可能需要很长时间才能意识到它需要减少并行量。在此之前,它会继续添加任务,并可能导致计算机崩溃
我在任务并行库的一位开发人员的一次演讲中学习了上述内容
指定MaxDegreeOfParallelism可能是最简单的方法。在任务并行库内部,Parallel.For和Parallel.Foreach遵循爬山算法确定操作应使用多少并行性
或多或少,他们从一个任务开始运行身体,然后移动到两个任务,依此类推,直到达到一个断点,他们需要减少任务数量
这对于快速完成的方法体非常有效,但是如果方法体需要很长时间才能运行,则可能需要很长时间才能意识到它需要减少并行量。在此之前,它会继续添加任务,并可能导致计算机崩溃
我在任务并行库的一位开发人员的一次演讲中学习了上述内容
指定MaxDegreeOfParallelism可能是最简单的方法。生产者/消费者模式主要用于只有一个生产者和一个消费者的情况
但是,您试图实现的(多个使用者)更符合工作列表模式。下面的代码是从犹他大学的一个并行编程类的UNIT2幻灯片“2C共享内存模式”中获取的,该代码可以在下载。
BlockingCollection工作列表;
取消源cts;
整数项计数
公开募捐
{
int num_工人=4;
//创建工作列表,填充初始工作
工作列表=新建BlockingCollection(
新的ConcurrentQueue(GetInitialWork());
cts=新的CancellationTokenSource();
itemcount=worklist.Count();
对于(int i=0;i0);
}最后{
如果(!cts.IsCancellationRequested)
BlockingCollection<Item> workList;
CancellationTokenSource cts;
int itemcount
public void Run()
{
int num_workers = 4;
//create worklist, filled with initial work
worklist = new BlockingCollection<Item>(
new ConcurrentQueue<Item>(GetInitialWork()));
cts = new CancellationTokenSource();
itemcount = worklist.Count();
for( int i = 0; i < num_workers; i++)
Task.Factory.StartNew( RunWorker );
}
IEnumberable<Item> GetInitialWork() { ... }
public void RunWorker() {
try {
do {
Item i = worklist.Take( cts.Token );
//blocks until item available or cancelled
Process(i);
//exit loop if no more items left
} while (Interlocked.Decrement( ref itemcount) > 0);
} finally {
if( ! cts.IsCancellationRequested )
cts.Cancel();
}
}
}
public void AddWork( Item item) {
Interlocked.Increment( ref itemcount );
worklist.Add(item);
}
public void Process( Item i )
{
//Do what you want to the work item here.
}