私のチームもこれに関する本を完成させました...
Microsoft® .NET による並列プログラミング:
マルチコア アーキテクチャでの分解と調整のための設計パターン
コリン・キャンベル、ラルフ・ジョンソン、エイド・ミラー、スティーブン・トゥーブ。トニー・ヘイによる序文
ドラフトとサンプルは、http: //parallelpatterns.codeplex.com/からダウンロードできます。
完全な書籍は、今月末に MSDN で、10 月に Amazon で入手できるようになります。
露骨なプラグインで申し訳ありませんが、内容は本当に役立つと思います.
アップデート...
あなたが選んだ問題 (以下) の質問に答えるために、Aggregation の実装 (リストから、リストの内容に基づいて集計を作成する) は、別の Parallel.ForEach よりもはるかにうまく PLinq にマップされます。PDF の p72 に Parallel.ForEach を使用した集計の例があります。
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;
}
}
プリンク:
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 Parallel Library は、Parallel Loop および Aggregation パターン以外にもサポートしています。マルチコア ハードウェアでの並列実行用にスケジュールされるタスクを構築できます (タスク並列処理パターン)。
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;
}
あるタスクの出力が別のタスクに供給されるタスクのグラフを作成できます (タスク グラフまたはフューチャー パターン)。
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);
}
上記のすべての機能には、例外処理とキャンセルのサポートも含まれていますが、わかりやすくするためにここでは示していません。