32

Parallel アプローチと Task アプローチの処理時間を比較するサンプル コードがあります。この実験の目的は、それらがどのように機能するかを理解することです。

だから私の質問は:

  1. Parallel が Task よりも高速に動作したのはなぜですか?
  2. この結果は、Task の代わりに Parallel を使用する必要があるということですか?
  3. どこでタスクを使用し、どこでパラレルを使用する必要がありますか?
  4. Parallel と比較して Task を使用する利点は何ですか?
  5. Task は ThreadPool.QueueUserWorkItem メソッドの単なるラップですか?

        public Task SomeLongOperation()
        {
            return Task.Delay(3000);
        }
    
        static void Main(string[] args)
        {
            Program p = new Program();
            List<Task> tasks = new List<Task>();
    
            tasks.Add(Task.Factory.StartNew(() => p.SomeLongOperation()));
            tasks.Add(Task.Factory.StartNew(() => p.SomeLongOperation()));
    
            var arr = tasks.ToArray();
    
            Stopwatch sw = Stopwatch.StartNew();
            Task.WaitAll(arr);
            Console.WriteLine("Task wait all results: " + sw.Elapsed);
            sw.Stop();
    
            sw = Stopwatch.StartNew();
            Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
            Console.WriteLine("Parallel invoke results: " + sw.Elapsed);
            sw.Stop();
    
            Console.ReadKey();
        }
    

ここに私の処理結果があります: 結果

編集:

コードを次のように変更しました。

    Program p = new Program();
    Task[] tasks = new Task[2];

    Stopwatch sw = Stopwatch.StartNew();
    tasks[0] = Task.Factory.StartNew(() => p.SomeLongOperation());
    tasks[1] = Task.Factory.StartNew(() => p.SomeLongOperation());

    Task.WaitAll(tasks);
    Console.WriteLine("Task wait all results: " + sw.Elapsed);
    sw.Stop();

    sw = Stopwatch.StartNew();
    Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
    Console.WriteLine("Parallel invoke results: " + sw.Elapsed);
    sw.Stop();

私の新しい結果:

新しい結果

編集 2: コードを Parallel.Invoke を最初に、Task.WaitAll を 2 番目に置き換えると、状況が大きく変わりました。Parallel が遅くなりました。自分の見積もりの​​誤りを考えさせられます。コードを次のように変更しました。

Program p = new Program();
Task[] tasks = new Task[2];

Stopwatch sw = null;
for (int i = 0; i < 10; i++)
{
    sw = Stopwatch.StartNew();
    Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
    string res = sw.Elapsed.ToString();
    Console.WriteLine("Parallel invoke results: " + res);
    sw.Stop();
}

for (int i = 0; i < 10; i++)
{
    sw = Stopwatch.StartNew();
    tasks[0] = Task.Factory.StartNew(() => p.SomeLongOperation());
    tasks[1] = Task.Factory.StartNew(() => p.SomeLongOperation());
    Task.WaitAll(tasks);
    string res2 = sw.Elapsed.ToString();
    Console.WriteLine("Task wait all results: " + res2);
    sw.Stop();
}

そして、ここに私の新しい結果があります:

ここに画像の説明を入力

ここに画像の説明を入力

これで、この実験はより明確になったと言えます。結果はほぼ同じです。Parallel の場合もあれば、Task の方が速い場合もあります。今私の質問は次のとおりです。

1. Task はどこで、Parallel はどこで使用する必要がありますか?

2. Parallel と比較して Task を使用する利点は何ですか?

3. Task は ThreadPool.QueueUserWorkItem メソッドの単なるラップですか?

これらの質問を明確にすることができる有用な情報は大歓迎です。

4

2 に答える 2

10

MSDNのこの記事の時点で編集:

Parallel と Task はどちらも ThreadPool のラッパーです。並列呼び出しも、すべてのタスクが完了するまで待機します。

あなたの質問に関連するもの:

Task、Parallel、または ThreadPool の使用は、並列タスクの実行に必要な制御の粒度によって異なります。個人的には慣れましTask.Factory.StartNew()たが、個人的な意見です。同じことが関係しますThreadPool.QueueUserWorkItem()

追加情報: Parallel.Invoke() および Task.Factory.StartNew() への最初の呼び出しは、内部の初期化により遅くなる可能性があります。

于 2013-04-19T10:38:45.227 に答える
5

非ジェネリック タスク (つまり、「戻り値のない無効なタスク」) を開始Waitし、それらに対してすぐに使用する場合は、Parallel.Invoke代わりに使用します。あなたの意図は読者にすぐにわかります。

次の場合にタスクを使用します。

  • すぐに待たない
  • 戻り値が必要です
  • 呼び出されたメソッドにパラメーターを与える必要があります
  • TaskCreationOptions機能が必要です
  • CancellationTokenまたは機能が必要TaskSchedulerで、使用したくないParallelOptions
  • 基本的に、より多くのオプションやコントロールが必要な場合

はい、これらのいくつかを回避できます。たとえばParallel.Invoke(() => p.OpWithToken(CancellationToken)、意図がわかりにくくなります。Parallel.Invoke可能な限り多くのCPUパワーを使用して一連の作業を行うためのものです。完了し、行き詰まりません。これは事前にわかっています。


しかし、あなたのテストは恐ろしいものです。危険信号は、長いアクションが 3000 ミリ秒待機することですが、テストにかかる時間は 10 分の 1 ミリ秒未満です。

Task.Factory.StartNew(() => p.SomeLongOperation());

StartNew は を受け取り、Actionこれを新しいmain Taskで実行します。アクションはサブタスク() => SomeLongOperation()を作成します。このサブタスクが作成された(完了していない) 後、呼び出しが返され、アクションが実行されます。したがって、メインは 10 ミリ秒後に既に完了していますが、参照されていない 2 つのサブタスクはバックグラウンドで実行されています。Parallel パスは、まったく追跡しない 2 つのサブタスクも作成し、返します。 TaskSomeLongOperation() Task

正しい方法はtasks[0] = p.SomeLongOperation();、実行中のタスクを配列に割り当てる です。次にWaitAll、このタスクの終了を確認します。

于 2016-10-03T18:22:29.867 に答える