C# 与固定数量的任务/线程并行使用IEnumerable

C# 与固定数量的任务/线程并行使用IEnumerable,c#,task-parallel-library,C#,Task Parallel Library,我有一个源IEnumerable,我想以并行方式处理它,有固定数量的任务/线程(接近处理器的数量),每个任务/线程从源抓取下一个项目并处理它,直到所有元素都被迭代 Parallel.For不是候选项,因为元素的数量未知 Parallel.ForEach不是候选项,因为即使指定了maxdegreeofpparallelism,is也会创建许多任务,因为此参数仅确保并发运行的最大任务数,而不确保创建的任务数 必须通知每个任务,源代码一直被遍历到其结束,以便它可以运行一些包装逻辑 源列表的元素不能保

我有一个源
IEnumerable
,我想以并行方式处理它,有固定数量的任务/线程(接近处理器的数量),每个任务/线程从源抓取下一个项目并处理它,直到所有元素都被迭代

  • Parallel.For
    不是候选项,因为元素的数量未知
  • Parallel.ForEach
    不是候选项,因为即使指定了
    maxdegreeofpparallelism
    ,is也会创建许多任务,因为此参数仅确保并发运行的最大任务数,而不确保创建的任务数
  • 必须通知每个任务,源代码一直被遍历到其结束,以便它可以运行一些包装逻辑
  • 源列表的元素不能保存在内存中,但必须连续处理和丢弃
听起来像是生产者/消费者的问题,生产者可以是单线程的,一旦完成IEnumerable,就不会添加更多的元素

使用第三方物流解决这个问题会是什么样子?我必须实现自己的可共享线程安全
IEnumerable
,还是框架提供了什么

编辑:这是我尝试使用
Parallel.ForEach
并指定
maxdegreeofpparallelism
,这不会阻止TPL创建许多任务

int nbTasks = 0;
Parallel.ForEach(positions, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
    () => { return new List<IPositionData>(); },
    (position, loop, list) =>
    {
        Thread.Sleep(1);
        list.Add(position);
        return list;
     },
     list => Interlocked.Add(ref nbTasks, 1));
Trace.WriteLine(string.Format("Tasks: {0}", nbTasks));
int nbTasks=0;
ForEach(位置,新的并行选项{MaxDegreeOfParallelism=Environment.ProcessorCount},
()=>{返回新列表();},
(位置、循环、列表)=>
{
睡眠(1);
列表。添加(位置);
退货清单;
},
list=>Interlocked.Add(参考nbTasks,1));
WriteLine(string.Format(“Tasks:{0}”,nbTasks));

注释:
positions
是我的源代码
IEnumerable
。我刚刚运行了这个,例如,nbTasks是64(而不是我的4核上预期的4).

您可以通过使用期望
ParallelOptions
对象的重载并设置
MaxDegreeOfParallelism
属性来限制
Parallel.ForEach
中的任务数。

您可以通过使用期望
ParallelOptions
的重载来限制
Parallel.ForEach中的任务数对象并设置
MaxDegreeOfParallelism
属性。

您可以限制
并行中的任务数。ForEach

   in maxNumberOfTasks = 4;
   Parallel.ForEach(collection, new ParallelOptions { MaxDegreeOfParallelism = maxNumberOfTasks}, 
                 i => {
                        //Your action here
                  });

您可以限制并行
中的任务数。ForEach

   in maxNumberOfTasks = 4;
   Parallel.ForEach(collection, new ParallelOptions { MaxDegreeOfParallelism = maxNumberOfTasks}, 
                 i => {
                        //Your action here
                  });

我已经尝试过了,但没有成功。尽管我将MaxDegreeOfParallelism指定给示例4,但创建的任务比示例4多得多。我猜,这是由于初始化,这不是唯一的CPU限制。我编辑了我的问题以反映这一点。@Dejan你能给我们看一下你的代码吗?我刚刚尝试了一个简单的例子,我可以验证在我的例子中,
MaxDegreeOfParallelism
是否正确工作。@Dejan我看到了你的问题
MaxDegreeOfParallelism
只确保并发任务的数量不超过您的总数,而不限制创建的任务数量。您是否可以修改
localInit
lambda,以便在后续任务之间重用复杂初始化的结果?您最多需要执行
Environment.ProcessorCount
初始化。如果没有,您可能需要编写自己的
TaskScheduler
。或者,请使用
parallel.For
,但不是根据项目的数量,而是根据要创建的并发任务的数量。然后您只需要同步对枚举器的访问。啊,这是一个很好的了解-现在我理解了
MaxDegreeOfParallelism
,谢谢。但总而言之,这又回到了我最初的问题。我是否必须实现我的单同步枚举器或自定义调度程序,或者框架是否提供了一些开箱即用的内容来覆盖我的场景。尽管我将MaxDegreeOfParallelism指定给示例4,但创建的任务比示例4多得多。我猜,这是由于初始化,这不是唯一的CPU限制。我编辑了我的问题以反映这一点。@Dejan你能给我们看一下你的代码吗?我刚刚尝试了一个简单的例子,我可以验证在我的例子中,
MaxDegreeOfParallelism
是否正确工作。@Dejan我看到了你的问题
MaxDegreeOfParallelism
只确保并发任务的数量不超过您的总数,而不限制创建的任务数量。您是否可以修改
localInit
lambda,以便在后续任务之间重用复杂初始化的结果?您最多需要执行
Environment.ProcessorCount
初始化。如果没有,您可能需要编写自己的
TaskScheduler
。或者,请使用
parallel.For
,但不是根据项目的数量,而是根据要创建的并发任务的数量。然后您只需要同步对枚举器的访问。啊,这是一个很好的了解-现在我理解了
MaxDegreeOfParallelism
,谢谢。但总而言之,这又回到了我最初的问题。我是否必须实现我的单同步枚举器或自定义调度程序,或者框架是否提供了一些开箱即用的内容来覆盖我的场景。