Моя команда также только что закончила книгу об этом ...
Параллельное программирование с Microsoft® .NET:
Шаблоны проектирования для разложения и координации на многоядерных архитектурах
Колин Кэмпбелл, Ральф Джонсон, Эд Миллер и Стивен Туб. Предисловие Тони Хей
Вы можете скачать черновик и образцы здесь: http://parallelpatterns.codeplex.com/
Полная книга будет доступна на MSDN позже в этом месяце и на Amazon в октябре.
Извиняюсь за откровенный плагин, но я думаю, что вы могли бы найти содержание действительно полезным.
Обновление ...
Чтобы ответить на ваш вопрос (ниже) на проблему, которую вы выбрали, реализация Aggregation (из списка, создающего агрегат на основе содержимого списка), оказывается, гораздо лучше отображает PLinq, чем альтернативный Parallel.ForEach. Есть пример агрегации с Parallel.ForEach на p72 в PDF.
Если вы просто хотите обновить содержимое набора с помощью PLinq, сопоставление будет намного проще. Например, следующий код перебирает список счетов, вычисляет тренд баланса и помечает счета с прогнозируемыми балансами, превышающими лимит овердрафта.
Последовательная:
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 может быть лучше, в некоторых случаях это просто вопрос предпочтений программиста.
Однако библиотека параллельных задач поддерживает больше, чем шаблоны параллельного цикла и агрегации. Это позволяет создавать задачи, которые будут запланированы для параллельного выполнения на многоядерном оборудовании (шаблон параллелизма задач):
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;
}
Позволяет создавать графики задач, в которых выходные данные одной задачи передаются в другую (график задач или шаблон Futures):
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);
}
Он также реализует несколько (потоковых) коллекций для использования в параллельной программе. Что позволяет прямую реализацию, например, шаблона Pipeline:
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);
}
Все вышеперечисленные функции также содержат поддержку обработки и отмены исключений, хотя для ясности я их здесь не показал.