C# 平行问题

C# 平行问题,c#,.net,multithreading,task-parallel-library,C#,.net,Multithreading,Task Parallel Library,我正在使用C#/VS2010中的Parallel.ForEach循环进行处理,我有几个问题 首先,我有一个流程,需要从远程Web服务提取信息,然后需要动态构建映像(GDI) 我有一个类,它使用两个主要方法Load()和CreateImage()将所有功能封装到一个对象中,所有GDI管理/WebRequests都在这个对象中“黑盒” 然后,我创建一个GenericList,其中包含需要处理的所有对象,并使用以下代码遍历该列表: try { Parallel.

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

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

我有一个类,它使用两个主要方法Load()和CreateImage()将所有功能封装到一个对象中,所有GDI管理/WebRequests都在这个对象中“黑盒”

然后,我创建一个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;
        }
现在我的问题是:

  • 考虑到列表中可能有一万项需要解析,上面的代码是实现这一点的最佳方法吗?还有其他想法吗
  • 我有一个问题,当我开始这个过程时,对象被创建/加载,图像创建得非常快,但在大约600个对象之后,这个过程开始爬行。它最终不会结束,这正常吗
  • 提前感谢:)
    Adam

    当循环体所做的工作受CPU限制时,带有默认设置的
    Parallel.ForEach
    方法最有效。如果您正在同步阻止或将工作移交给另一方,调度程序会认为CPU仍然不忙,并不断填充更多任务,试图使用系统中的所有CPU


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

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

    以下是一个单一生产商和多个消费者的示例。
    (如果消费者的速度比生产者快,您可以使用普通的foreach而不是parallel.foreach)

    var sources=BlockingCollection();
    var producer=Task.Factory.CreateNew(
    () => {
    foreach(MyGenericList中的变量项){
    var data=webservice.FetchData(项);
    来源.添加(数据)
    }
    sources.CompleteAdding();
    }
    )
    Parallel.ForEach(sources.GetConsumingPartitioner(),
    数据=>{
    CreateImage(数据);
    });
    
    (GetConsumingPartitioner扩展是的一部分)

    编辑一个更完整的示例

    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();
        });
    
    var sources=BlockingCollection();
    var ProduceProptions=new ParallelOptions{MaxDegreeOfpParallelism=5};
    var consumerOptions=new ParallelOptions{maxdegreeofpparallelism=-1};
    var producers=Task.Factory.CreateNew(
    () => {
    Parallel.ForEach(我的通用列表、产品目录、,
    myObject=>{
    myObject.DoLoad()
    sources.Add(myObject)
    });
    sources.CompleteAdding();
    });
    Parallel.ForEach(sources.GetConsumingPartitioner(),consumerOptions,
    myObject=>{
    myObject.CreateImage();
    myObject.Dispose();
    });
    

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

    有一个catch块除了抛出它捕获的异常之外什么都不做,这有什么意义?嗨,这刚好足够显示我在may应用程序中所做的事,显然,没有人对我的错误处理感兴趣,因为它与问题无关:)这很公平,但我会完全忽略catch块(显然是第二个块)——特别是因为你只是在草拟一个示例。嗨,谢谢你的帮助。在上面的例子中,我将不得不等待,直到从webservice检索到每个项目,这可能需要一段时间。我希望在一个单独的线程中填充我的对象/创建图像,每次5个,因为它通过列表来加快速度。不,示例中有一个线程连续检索项目。一旦取回一个项目,消费者部分就开始处理它(同时取回下一个项目)。如果您认为同时获取五个项目更快,只需使用多个生产者即可。(我将更新示例)再次感谢您的帮助;我遇到的一个问题是ParallelExtensionsExtras.dll,我已经下载了它,添加了一个引用,但是GetConsumingPartitioner似乎从未出现过。我得到了一份可以信赖的工作,没问题。有什么想法吗?好的,谢谢;这似乎是做这件事的正确方法
    code
    Parallel.ForEach(BlockingCollectionExtensions.GetConsumingPartitioner(sources),consumerOptions,myObject=>{
    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();
        });