3
var finalList = new List<string>(); 
var list = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ................. 999999}; 

var init = 0; 
var limitPerThread = 5; 

var countDownEvent = new CountdownEvent(list.Count); 

for (var i = 0; i < list.Count; i++) 
{ 
    var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
    new Thread(delegate() 
        { 
         Foo(listToFilter); 
         countDownEvent.Signal(); 
        }).Start();  
    init += limitPerThread; 
} 

//wait all to finish 
countDownEvent.Wait(); 


private static void Foo(List<int> listToFilter) 
{ 
    var listDone = Boo(listToFilter); 
    lock (Object) 
    { 
     finalList.AddRange(listDone); 
    } 
} 

これはしません:マルチスレッドでは単純なマルチタスクが動作しないのはなぜですか?

var taskList = new List<Task>(); 

for (var i = 0; i < list.Count; i++) 
{ 
    var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
    var task = Task.Factory.StartNew(() => Foo(listToFilter)); 
    taskList.add(task); 
    init += limitPerThread; 
} 

//wait all to finish 
Task.WaitAll(taskList.ToArray()); 

このプロセスは、最終的には、少なくとも700個のスレッドを作成する必要があります。スレッドを使用して実行すると、すべてのスレッドが動作し、作成されます。しかし、Taskでは、そうではありません。それは、開始マルチプルTasks asyncのようではないようです。

私は本当に理由を知りたいと思っています。

EDIT

(示唆されるように)PLINQと別のバージョン。

var taskList = new List<Task>(list.Count); 
Parallel.ForEach(taskList, t => 
        { 
         var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
         Foo(listToFilter); 
         init += limitPerThread; 
         t.Start(); 
        }); 
Task.WaitAll(taskList.ToArray()); 

EDIT2:

public static List<Communication> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 

     try 
     { 
      for (var i = 0; i < listIps.Count(); i++) 
      { 
       var oPing = new Ping().Send(listIps.ElementAt(i).IpAddress, 10000); 
       if (oPing != null) 
       { 
        if (oPing.Status.Equals(IPStatus.TimedOut) && listIps.Count() > i+1) 
         continue; 
        if (oPing.Status.Equals(IPStatus.TimedOut)) 
        { 
         communication.Result = "NOK"; 
         break; 
        } 
        communication.Result = oPing.Status.Equals(IPStatus.Success) ? "OK" : "NOK"; 
        break; 
       } 
       if (listIps.Count() > i+1) 
        continue; 
       communication.Result = "NOK"; 
       break; 
      } 
     } 
     catch 
     { 
      communication.Result = "NOK"; 
     } 
     finally 
     { 
      listResult.Add(communication); 
     } 
    } 

    return listResult; 
} 
+0

要素を1つずつ処理できますか?つまり、リストではなく、単一の要素に対して 'Boo()'を呼び出すことができますか? –

+0

私はそれらを1つずつ処理することはできません。複数のipsアドレスにpingを実行するプロセスです... 2分以内ですべてを(+ - 3000)pingする必要があります。そして結果を保存する必要があります。 –

+2

待って...ちょうど**何を 'Boo()'は、実際にしますか?単に「ping」プロセスを生成するのではありませんか? –

答えて

6

Task sがマルチスレッドされません。彼らはとすることができますが、ほとんどの場合、実際には1つのスレッドで逆多重化に使用されます。

マルチスレッドのタスクを使用するには、Parallel LINQを使用することをお勧めします。典型的な - それは、それをこのように考えて、Taskasyncを理解するためになど、インテリジェントあなたのリストのパーティションのみCPUコアがArなどのように多くのスレッドを産卵、既にその中に多くの最適化、


を持っていますワークロードには待機する必要のあるIOが含まれることがよくあります。ファイルを読んだり、Webサービスを照会したり、データベースにアクセスしたりすることもあります。要点は、あなたのスレッドは、あなたが遠く離れたいくつかの宛先から応答を受け取るまで(CPUサイクルで少なくとも)待つことになります。

Olden Days™では、その応答が来るまでスレッドがロックダウン(中断)していたことを意味していました。その間に何か他のことをやりたければ、新しいスレッドを生成する必要がありました。それは実行可能ですが、それほど効率的ではありません。各OSスレッドは、それと重大なオーバーヘッド(メモリ、カーネルリソース)を持ちます。そして、あなたはCPUを積極的に焼くいくつかのスレッドで終わる可能性があります。つまり、それぞれが少しのCPU時間を得るようにOSを切り替える必要があり、これらの「コンテキストスイッチ」は非常に高価です。

asyncこのワークフローを変更します。これで、同じスレッドで複数のワークロードを実行できるようになりました。 1つの作業は遠いソースからの結果であり、別の作業に入り、そのスレッドを使用して他の何かを行うことができます。その第2のワークロードが自身のawaitに到達すると、最初のワークロードが起動して処理を続行できます。

結局のところ、CPUコアより多くのスレッドを生成することは意味がありません。あなたはそうしたやり方でもっと仕事をするつもりはありません。ちょうど反対です - スレッドを切り替えるのに多くの時間が費やされ、有用な作業には時間がかかりません。

これは、もともとTask/async/awaitのために設計されたものです。しかし、Parallel LINQはこれを利用してマルチスレッド化のために再利用しています。この場合、この方法で見ることができます。他のスレッドは、あなたのメインスレッドがあなたのメインスレッドが待っている "遠い目的地"です。

+0

良いテキスト、ブロ...私もPLINQを試みたが、成功なし。私は実装内の質問を編集するつもりです。あなたが見ていただければ幸いです。 –

+2

"タスクはマルチスレッドではありません" - いいえ、必ずしもそうではありませんが、StartNew()は少なくともThreadPoolにプッシュアウトすることを意味します。 –

+0

trueですが、async ==マルチスレッドというのはよくある誤解です。そしてOPもそう思っていたようだ。 –

5

タスクはスレッドプール上で実行されます。これは、少数のスレッドが多数のタスクを処理することを意味します。あなたはマルチスレッド化していますが、生成されたすべてのタスクのスレッドはありません。

タスクを使用する必要があります。 CPUと同じ数のスレッドを使用することを目指すべきです。一般的に、スレッドプールはこれを実行しています。

0

パフォーマンスをどのように測定しましたか? 700スレッドが700タスクが4スレッドで実行されるよりも速く動作すると思いますか?いいえ、そうではありません。

は、それはあなたがこの思い付いたきっかけ

非同期のない開始倍数の作業のように思えますか?コメントやその他の答えに示唆されているように、700スレッドを作成した後は、スレッドがプロセッサ時間だけ互いに競合するため、システムパフォーマンスが低下します。 。

したがって、SendPingAsyncバージョンのFooメソッドに、IO操作用にasync/awaitを追加する必要があります。

:あなたのコードで

public static async Task<List<Communication>> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 

     try 
     { 
      var ping = new Ping(); 
      communication.Result = "NOK"; 

      for (var i = 0; i < listIps.Count(); i++) 
      { 
       var oPing = await ping.SendPingAsync(listIps.ElementAt(i).IpAddress, 10000); 
       if (oPing != null) 
       { 
        if (oPing.Status.Equals(IPStatus.Success) 
        { 
         communication.Result = "OK"; 
         break; 
        } 
       } 
      } 
     } 
     catch 
     { 
      communication.Result = "NOK"; 
     } 
     finally 
     { 
      listResult.Add(communication); 
     } 
    } 

    return listResult; 
} 

他の問題はPLINQバージョンはスレッドセーフではないということです:あなたはfor条件ブロックでそれを行う - listIps.Count() > i + 1条件のために多くのチェックが役に立たないとしても、あなたの方法は、simplyfiedすることができ

init += limitPerThread; 

これは、並列実行中に失敗する可能性があります。あなたはthis answerのように、いくつかのヘルパーメソッドを導入することができる:

private async Task<List<PingReply>> PingAsync(List<Communication> theListOfIPs) 
{ 
    Ping pingSender = new Ping(); 
    var tasks = theListOfIPs.Select(ip => pingSender.SendPingAsync(ip, 10000)); 
    var results = await Task.WhenAll(tasks); 

    return results.ToList(); 
} 

と(簡単にするために取り除かtry/catchロジック)は、このチェックの種類の操作を行います。

public static async Task<List<Communication>> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 
     var check = await PingAsync(listIps); 
     communication.Result = check.Any(p => p.Status.Equals(IPStatus.Success)) ? "OK" : "NOK"; 
    } 
} 

をそして、あなたはおそらくであることのためTask.StartNewの代わりにTask.Runを使用する必要がありますUIスレッドをブロックしていないことを確認してください。

関連する問題