.net Параллельный и последовательный цикл - PullRequest
3 голосов
/ 09 марта 2012

Мы хотели бы построить схему вокруг определенных циклов в нашем решении, которая позволит им работать последовательно или параллельно, в зависимости от факторов. Ниже приводится его общая форма.

Поскольку параллельные коллекции не имеют общего интерфейса с обычными коллекциями, нам нужен какой-то адаптер для написания общего кода.

В частности, в отношении использования делегата addFunc в теле цикла, есть ли что-нибудь, что в конечном итоге может вызвать проблемы в долгосрочной перспективе, которые мы могли бы пропустить? На данный момент он работает нормально, но ....?

Action<SomeType> addFunc;

if(runInParallel)
{
   addFunc = concurrentBag.Add;
   loopDelegate = Parallel.ForEach;
}
else
{
   addFunc = iList.Add;
   loopDelegate = Serial.ForEach; // wrapper delegate for foreach
}

loopDelegate(source, item =>
{
   SomeType result = longRunningTask(item);
   ...
   addFunc(result); // will this 
});

1 Ответ

2 голосов
/ 16 апреля 2012

Любопытно, почему бы не использовать TPL в .NET 4.0? http://msdn.microsoft.com/en-us/library/dd537609.aspx

Существует превосходный технический документ, который учитывается при разработке TPL. Если вы не можете использовать .NET 4, вам следует взглянуть на этот документ и рассмотреть некоторые из полученных ошибок.

Обновлено на основе комментариев, указывающих на очевидное.

Я бы использовал синтаксический сахар, как,

ForEach<Tsource>(Predicate<IEnumerable<TSource>> isParallel, IEnumerable<TSource> source, Action<TSource> body)
{
    if(isParallel(source))
    {
        Parallel.ForEach<TSource>(source, body);
    }
    else
    {
        foreach (TSource element in source)
        {
            body(element);
        }
    }
}

У вашей реализации есть два основных преимущества.

  1. Вы перечисляете два раза, один раз, чтобы добавить элементы для зацикливания, и второй раз во время выполнения.
  2. Это не сразу очевидно, но вы останавливаете Parallel.ForEach и не можете использовать самый эффективный метод получения. Parallel.ForeEach не всегда будет использовать GetEnumerator в IEnumerable. Конструктор перечислителя не является параллельным, если ваш элемент реализует IList, Parallel.ForEach будет использовать индексатор, чтобы позволить каждому потоку обращаться к элементу в источнике, не дожидаясь, пока перечислитель выполнит итерацию по списку. ConcurrentBag не реализует IList.

Это бумага, о которой я говорил, http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=19222.

Это все очень хорошо читается, если вы воплощаете это, но обратите особое внимание, Страницы 1 [5-7], 26, 3 [0-2].

Синтаксический сахар означает, что вы можете называть его как с TPL,

MyParllelLibrary.ForEach( (list) => true, list), item =>
{
    // What my code does
});
...