5

更新-質問のタイトルを変更して、私が本当に求めているものを反映させました

次のコードについて考えてみます。

// this query generates 12 instances of Func<int>, which each when executed
// print something to the console and wait for 1 second.
var actions = Enumerable.Range(0, 12).Select(i => new Func<int>(() =>
{
    Console.WriteLine("{0} - waiting 1 sec", i);
    Thread.Sleep(1000);
    return 1;
}));

// define a parallel query. Note the WithDegreeOfParallelism call here.
var query = from action in actions.AsParallel().WithDegreeOfParallelism(12)
            select action();

// execute, measuring total duration
var stopw = Stopwatch.StartNew();
query.ToList();
Console.WriteLine(stopw.Elapsed);
Console.WriteLine(Environment.ProcessorCount); // 3 on my machine

の呼び出しを省略するとWithDegreeOfParallelism、これは4つのチャンクで実行され、合計で約4秒かかります。これは、CPUカウントが3であるため、予想されることです。

ただし、4を超える番号で呼び出すWithDegreeOfParallelismと、常に3つのチャンクが取得され、合計時間は3秒未満にはなりません。値が12の場合、合計時間は(1秒強)1秒になると思います。

私は何が欠けていますか?そして、CPUを集中的に使用しない4つ以上のタスクの並列実行を強制するにはどうすればよいですか?

更新:もちろん手動でスレッドをスピンアップすることに戻ることもできますが、新しいPFXライブラリによってこれが少し簡単になることを期待していました...とにかく、以下のコードは約1秒の合計実行時間を与えます

List<Thread> threads = new List<Thread>();
for (int i = 0; i < 12; i++)
{
    int i1 = i;
    threads.Add(new Thread(() =>
    {
        Console.WriteLine(i1);
        Thread.Sleep(1000);
    }));
}
threads.ForEach(t => t.Start());
threads.ForEach(t => t.Join());
4

3 に答える 3

3

オプションを使用して、並列ループで新しいタスクを開始してみてくださいTaskCreationOptions.LongRunning。スレッドプールのスレッドが使用可能になるまで待つのではなく、すぐに開始します。

于 2011-07-27T12:45:47.987 に答える
2

私が言ったように、WithDegreeOfParallelismは上限のみを設定しています。タスクを10から100に増やしてみてください。100個すべてに対して約10秒になります。あなたのコードは、より小さな操作でより多くのタスクに適しています。タスク内に追加すると、 Console.WriteLine("{0} threads " ,Process.GetCurrentProcess().Threads.Count);作成されたスレッドの数を確認できます(スレッド数は、plinqで作成されたスレッドの数ではありません。どのように増加するかを確認してください)。

PLinqを使用して並列処理を行う方法はたくさんあります。この記事http://msdn.microsoft.com/en-us/library/dd997411.aspxをお読みください。パフォーマンスを向上させるには、関連する要件に最適な方法を選択する必要があります。

于 2011-07-27T16:26:30.640 に答える
2

WithDegreeOfParallelismは、PLINQが作成するタスクの数を指定しますが、必ずしも使用されるスレッドの数を指定するわけではありません。

タスクはThreadPoolの作業項目として実行されるため、クエリを実行するスレッドの数はThreadPoolのサイズに制限されます。ThreadPoolは必要に応じてスレッドを追加しますが、時間がかかる場合があります-ThreadPoolは1秒あたり2スレッド程度を追加する場合があります。

スレッドをThreadPoolにすばやく追加する場合は、SetMinThreadsメソッドを使用できます。このコードをコードの先頭に置くと、テストは1秒ほどで完了するはずです。

int prevThreads, prevPorts;
ThreadPool.GetMinThreads(out prevThreads, out prevPorts);
ThreadPool.SetMinThreads(12, prevPorts);

必要なスレッドの数を決定してから、SetMinThreadsとSetMaxThreadsを使用してThreadPoolサイズの境界を設定できます。

于 2011-07-28T07:36:07.757 に答える