Моя команда также только что закончил книгу по этому вопросу ...
Параллельное программирование с Microsoft® .NET: Шаблоны для разложения и координации на многоядерных архитектурах
Colin Кэмпбелл, Ральф Джонсон, Аде Миллер и Стивен Туб.Предисловие Тони Эй
Вы можете скачать проект и образцы здесь: http://parallelpatterns.codeplex.com/
Полная книга будет доступна на MSDN позже в этом месяце и на Amazon в октябре.
Извинения за вопиющую вилку, но я думаю, вы могли бы найти контент действительно полезным.
Update ...
Чтобы ответить на ваш вопрос (ниже) проблемы вы выбрали, реализацию агрегации (из списка создать агрегат на основе содержимого списка) происходит на карту гораздо лучше PLinq, чем альтернатива, Parallel.ForEach. Пример агрегации с Parallel.ForEach на p72 PDF.
Если вы просто хотите обновить содержимое набора с использованием PLinq, сопоставление будет намного проще. Например, следующий код перебирает список учетных записей, вычисляет тренды баланса и флаговые счета с прогнозируемыми остатками больше, чем их лимит овердрафта.
Sequential:
static void UpdatePredictionsSequential(AccountRepository accounts)
{
foreach (Account account in accounts.AllAccounts)
{
Trend trend = SampleUtilities.Fit(account.Balance);
double prediction = trend.Predict(account.Balance.Length + NumberOfMonths);
account.SeqPrediction = prediction;
account.SeqWarning = prediction < account.Overdraft;
}
}
PLINQ:
static void UpdatePredictionsPlinq(AccountRepository accounts)
{
accounts.AllAccounts
.AsParallel()
.ForAll(account =>
{
Trend trend = SampleUtilities.Fit(account.Balance);
double prediction = trend.Predict(account.Balance.Length + NumberOfMonths);
account.PlinqPrediction = prediction;
account.PlinqWarning = prediction < account.Overdraft;
});
}
Parallel.ForEach:
static void UpdatePredictionsParallel(AccountRepository accounts)
{
Parallel.ForEach(accounts.AllAccounts, account =>
{
Trend trend = SampleUtilities.Fit(account.Balance);
double prediction = trend.Predict(account.Balance.Length + NumberOfMonths);
account.ParPrediction = prediction;
account.ParWarning = prediction < account.Overdraft;
});
}
В некоторых случаях, PLINQ может быть наиболее выразительным выбором. В других Parallel.For/ForEach может быть лучше, в некоторых случаях это в значительной степени зависит от предпочтений программиста.
Однако параллельная библиотека задач поддерживает больше шаблонов параллельной петли и агрегации. Это позволяет построить задачи, которые будут запланированы для параллельного выполнения на многоядерной аппаратной (Task параллелизм образов):
static int ParallelTaskImageProcessing(Bitmap source1, Bitmap source2,
Bitmap layer1, Bitmap layer2, Graphics blender)
{
Task toGray = Task.Factory.StartNew(() => SetToGray(source1, layer1));
Task rotate = Task.Factory.StartNew(() => Rotate(source2, layer2));
Task.WaitAll(toGray, rotate);
Blend(layer1, layer2, blender);
return source1.Width;
}
Это позволяет создавать графики задач, где выход одной задачи, подаваемые в другой (Task Graph или график фьючерсов):
public static int Example4()
{
var a = 22;
var cf = Task<int>.Factory.StartNew(() => F2(a));
var df = cf.ContinueWith((t) => F3(t.Result));
var b = F1(a);
var f = F4(b, df.Result);
return f;
}
Где F1-F4 - это функции, у которых входы и выходы имеют зависимости.
Он поддерживает создание деревьев зависимых задач, для разделяй и властвуй проблемы, как сортировка (шаблон динамической параллелизм задач):
static void ParallelWalk<T>(Tree<T> tree, Action<T> action)
{
if (tree == null) return;
var t1 = Task.Factory.StartNew(
() => action(tree.Data));
var t2 = Task.Factory.StartNew(
() => ParallelWalk(tree.Left, action));
var t3 = Task.Factory.StartNew(
() => ParallelWalk(tree.Right, action));
Task.WaitAll(t1, t2, t3);
}
Он также реализует несколько (потоко) коллекции для использования в параллельной программе. Что позволяет прямой реализации, например, модель трубопровода:
static void Chapter7Example01Pipeline(int seed)
{
Console.Write("Begin Pipelined Sentence Builder");
var buffer1 = new BlockingCollection<string>(BufferSize);
var buffer2 = new BlockingCollection<string>(BufferSize);
var buffer3 = new BlockingCollection<string>(BufferSize);
var f = new TaskFactory(TaskCreationOptions.LongRunning,
TaskContinuationOptions.None);
var stage1 = f.StartNew(() => ReadStrings(buffer1, seed));
var stage2 = f.StartNew(() => CorrectCase(buffer1, buffer2));
var stage3 = f.StartNew(() => CreateSentences(buffer2, buffer3));
var stage4 = f.StartNew(() => WriteSentences(buffer3));
Task.WaitAll(stage1, stage2, stage3, stage4);
}
Все вышеперечисленные функции содержат также поддержку обработки исключений и отмены, хотя для ясности я не показал им здесь.
Существует несколько примеров в блоге групп http://blogs.msdn.com/pfxteam, также имеется тонна образцов, доступных по адресу http://code.msdn.microsoft.com/ParExtSamples. – Rick