Parallel.ForEach问题

时间:2011-05-17 21:03:40

标签: c# .net multithreading task-parallel-library

我在C#/ VS2010中使用Parallel.ForEach循环进行处理,我有几个问题。

首先,我有一个流程需要从远程Web服务中提取信息,然后需要动态构建映像(GDI)。

我有一个类将所有功能封装到一个对象中,使用两个主要方法Load()和CreateImage(),并在此对象中包含所有GDI管理/ WebRequests“blackboxed”。

然后我创建一个包含所有需要处理的对象的GenericList,并使用以下代码遍历列表:

try
        {
            Parallel.ForEach(MyLGenericList, ParallelOptions, (MyObject, loopState) =>
            {                                       

                    MyObject.DoLoad();
                    MyObject.CreateImage();
                    MyObject.Dispose();

                if (loopState.ShouldExitCurrentIteration || loopState.IsExceptional)
                    loopState.Stop();
            });
        }
        catch (OperationCanceledException ex)
        {
            // Cancel here
        }
        catch (Exception ex)
        {
            throw ex;
        }

现在我的问题是:

  1. 鉴于列表中可能有一万个项要解析,上面的代码是最好的方法吗?欢迎任何其他想法
  2. 我有一个问题,当我开始这个过程时,创建/加载对象并快速创建图像,但是在大约六百个对象之后,该过程开始爬行。它最终没有完成,这是正常的吗?
  3. 提前致谢:) 亚当

2 个答案:

答案 0 :(得分:4)

我不确定并行下载数据是个好主意,因为它会阻塞很多线程。将您的任务拆分为生产者和消费者。然后你可以分别并行化它们。

以下是单个生产者和多个消费者的示例 (如果消费者比生产者更快,你可以使用普通的foreach而不是parallel.ForEach)

var sources = BlockingCollection<SourceData>();
var producer = Task.Factory.CreateNew(
    () => {
        foreach (var item in MyGenericList) {
            var data = webservice.FetchData(item);
            sources.Add(data)
        }
        sources.CompleteAdding();
    }
)
Parallel.ForEach(sources.GetConsumingPartitioner(),
                 data => {
                     imageCreator.CreateImage(data);
                 });

(GetConsumingPartitioner扩展是ParallelExtensionsExtras

的一部分

修改更完整的示例

var sources = BlockingCollection<SourceData>();

var producerOptions = new ParallelOptions { MaxDegreeOfParallelism = 5 };
var consumerOptions = new ParallelOptions { MaxDegreeOfParallelism = -1 };

var producers = Task.Factory.CreateNew(
    () => {
        Parallel.ForEach(MyLGenericList, producerOptions, 
            myObject => {
                myObject.DoLoad()
                sources.Add(myObject)
            });
        sources.CompleteAdding();
    });
Parallel.ForEach(sources.GetConsumingPartitioner(), consumerOptions,
    myObject => {
        myObject.CreateImage();
        myObject.Dispose();
    });

使用此代码,您可以优化并行下载量,同时保持CPU忙于图像处理。

答案 1 :(得分:1)

当循环体所做的工作是CPU绑定时,使用默认设置的Parallel.ForEach方法效果最佳。如果您同步阻止或将工作交给另一方,则调度程序认为CPU仍然不忙并且不断填写更多任务,并努力使用系统中的所有CPU。

在您的情况下,您需要选择合理数量的重叠下载并行发生,并在ForEach选项中设置该值,因为您不会在循环中使CPU饱和。