25

非同期のサンプル コードを見ていて、実装方法にいくつかの問題があることに気付きました。コードを見ながら、リストを通常どおりにループするよりも、as parallel を使用してリストをループする方が効率的かどうか疑問に思いました。

私が知る限り、パフォーマンスの違いはほとんどなく、どちらもすべてのプロセッサを使い果たし、完了するまでの時間はほぼ同じです。

これはそれを行う最初の方法です

var tasks= Client.GetClients().Select(async p => await p.Initialize());

そしてこれが2つ目

var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());

両者の間に違いはないと仮定するのは正しいですか?

完全なプログラムは以下にあります

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            RunCode1();
            Console.WriteLine("Here");
            Console.ReadLine();

            RunCode2();
            Console.WriteLine("Here");

            Console.ReadLine();

        }

        private async static void RunCode1()
        {
            Stopwatch myStopWatch = new Stopwatch();
            myStopWatch.Start();

            var tasks= Client.GetClients().Select(async p => await p.Initialize());

            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds);
            myStopWatch.Stop();
        }
        private async static void RunCode2()
        {
            Stopwatch myStopWatch = new Stopwatch();
            myStopWatch.Start();
            var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds);
            myStopWatch.Stop();
        }
    }
    class Client
    {
        public static IEnumerable<Client> GetClients()
        {
            for (int i = 0; i < 100; i++)
            {
                yield return new Client() { Id = Guid.NewGuid() };
            }
        }

        public Guid Id { get; set; }

        //This method has to be called before you use a client
        //For the sample, I don't put it on the constructor
        public async Task Initialize()
        {
            await Task.Factory.StartNew(() =>
                                      {
                                          Stopwatch timer = new Stopwatch();
                                          timer.Start();
                                          while(timer.ElapsedMilliseconds<1000)
                                          {}
                                          timer.Stop();

                                      });
            Console.WriteLine("Completed: " + Id);
        }
    }
}
4

3 に答える 3

27

識別可能な違いはほとんどないはずです。

あなたの最初のケースでは:

var tasks = Client.GetClients().Select(async p => await p.Initialize());

実行中のスレッドは、(一度に 1 つずつ)Initializeクライアント リスト内の各要素に対して実行を開始します。Initializeメソッドをすぐにスレッド プールのキューに入れ、未完了の を返しますTask

あなたの2番目のケースでは:

var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());

実行中のスレッドは、スレッド プールに分岐し、(並行して)Initializeクライアント リスト内の各要素の実行を開始します。Initializeの動作は同じです。メソッドをすぐにスレッド プールのキューに入れ、戻ります。

スレッド プールへのメソッドのキューイングと uncompleted の戻りという少量のコードのみを並列化するため、2 つのタイミングはほぼ同じですTask

Initialize最初awaitAsParallel. _

すべてのasyncメソッド (およびラムダ) は同期的に実行されることを覚えておいてください (公式の FAQまたは私自身の紹介記事を参照してください)。

于 2012-09-24T12:24:20.617 に答える
7

唯一の大きな違いがあります。

次のコードでは、自分でパーティショニングを実行しています。つまり、への呼び出しから返される からTaskアイテムごとに 1 つのオブジェクトを作成しています。IEnumerable<T>GetClients()

var tasks= Client.GetClients().Select(async p => await p.Initialize());

2 番目の呼び出しでAsParallelは、内部的にTaskインスタンスを使用して のパーティションを実行IEnumerable<T> Task、 lambda から返されるイニシャルを取得しasync p => await p.Initialize()ます。

var tasks = Client.GetClients().AsParallel().
    Select(async p => await p.Initialize());

async最後に、ここで/を使用しても実際には何もしていませんawait。確かに、コンパイラはこれを最適化するかもしれませんが、a を返すメソッドを待ってTaskから、ラムダを介して何も返さない継続を返すだけです。とはいえ、への呼び出しInitializeはすでに を返しているTaskため、シンプルに保ち、次のようにするのが最善です。

var tasks = Client.GetClients().Select(p => p.Initialize());

Taskインスタンスのシーケンスが返されます。

于 2012-09-24T12:22:08.560 に答える
0

上記の2つの回答を改善するために、これは待機可能な非同期/スレッド実行を取得する最も簡単な方法です:

var results = await Task.WhenAll(Client.GetClients().Select(async p => p.Initialize()));

これにより、個別のスレッドがスピンされ、最後に結果が得られることが保証されます。それが誰かを助けることを願っています。これは非常に明白ではなく、AsParallel() 関数が必要なように見えますが、async/await を使用していないため、これを適切に理解するのにかなりの時間がかかりました。

于 2018-05-15T17:59:47.663 に答える