Parallel.For Todas as perguntas

9

Estou usando um loop Parallel.ForEach em C # / VS2010 para fazer o processamento e tenho algumas perguntas.

Primeiro de tudo, eu tenho um processo que precisa extrair informações de um serviço remoto e, em seguida, precisa construir imagens (GDI) on the fly.

Eu tenho uma classe que encapsula toda a funcionalidade em um único objeto com dois métodos principais, Load () e CreateImage (), com todos os GDI management / WebRequests "blackboxed" dentro deste objeto.

Eu então crio um GenericList que contém todos os objetos que precisam ser processados e eu percorro a lista usando o seguinte código:

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;
        }

Agora minhas perguntas são:

  1. Dado que pode haver dez mil itens na lista para analisar, o código acima é a melhor maneira de abordar isso? Quaisquer outras ideias mais que bem-vindas
  2. Eu tenho um problema em que, quando inicio o processo, os objetos são criados / carregados e as imagens criadas muito rapidamente, mas após cerca de 600 objetos, o processo começa a ser rastreado. Não acaba, isso é normal?

Obrigado antecipadamente :) Adam

    
por user758136 17.05.2011 в 23:03
fonte

2 respostas

4

Não tenho certeza se o download de dados em paralelo é uma boa ideia, pois bloqueará muitos segmentos. Divida sua tarefa em um produtor e um consumidor. Então você pode paralelizar cada um deles separadamente.

Aqui está um exemplo de um único produtor e vários consumidores.
(Se os consumidores são mais rápidos do que o produtor, você pode simplesmente usar um foreach normal em vez de paralelo. Para cada um)

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);
                 });

(a extensão GetConsumingPartitioner faz parte do ParallelExtensionsExtras )

Editar Um exemplo mais completo

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();
    });

Com este código, você pode otimizar a quantidade de downloads paralelos, mantendo a CPU ocupada com o processamento da imagem.

    
por adrianm 17.05.2011 / 23:57
fonte
1

O método Parallel.ForEach com as configurações padrão funciona melhor quando o trabalho que o corpo do loop faz é vinculado à CPU. Se você estiver bloqueando ou entregando o trabalho para outra pessoa de forma síncrona, o agendador acha que a CPU ainda não está ocupada e continua enchendo mais tarefas, tentando usar todas as CPUs no sistema.

No seu caso, você precisa apenas escolher um número razoável de downloads sobrepostos para ocorrer em paralelo e definir esse valor em suas opções ForEach , porque você não saturará as CPUs com o loop.

    
por Rick Sladkey 17.05.2011 / 23:37
fonte