2016-07-25 9 views
2

私はかなり単純なRaytracerを書いています。プログラムはシングルスレッドなので実行時の制限があります。私がGoogleから見つけた結果は、このタイプの質問に2〜3つのタスクで対処して回答しています。多量のタスク/スレッドを作成してそれらがすべて完了するのを待つ

class Program 
{ 
    static void Main(string[] args) 
    { 
     var taskList = new List<Task>(); 

     taskList.Add(Task.Factory.StartNew(() => doStuff())); 
     taskList.Add(Task.Factory.StartNew(() => doStuff())); 
     taskList.Add(Task.Factory.StartNew(() => doStuff())); 

     Task.WaitAll(taskList); 

     Console.WriteLine("All threads complete"); 
    } 

    static void doStuff() 
    { 
     //do stuff here 
    } 
} 

純粋に実装されていれば、少なくとも10,000個のスレッドがあります。上記のソリューションは、このシナリオでは最適なソリューションとは思われません。これをサポートしている標準ライブラリの一部がありますか、または実装されているようなNugetパッケージがありますか?また、私は馬鹿だと思うかもしれませんし、Listの> 10,000スレッドはまったく問題ではありません。カットオフ時に問題が発生します。いくつかのインスタンスでは12500000個のタスク/スレッドが必要になりますが、これはリストには多すぎます。

以下は、私が新しいスレッド/タスクを作成する方法の概略です。あなたが複数のスレッドを使用して処理したい値のリスト(またはその他のIEnumerable<T>)をお持ちの場合

for (var x = 0; x < image.Width; x++) { 
    for (var y = 0; y < image.Height; y++) { 
     var coordinates = new Vector3(x, y, 0); 
     var task = new Task(() => { 
      RenderSinglePixel(coordinates); 
     }); 
    } 
} 
+2

の全体の速度が遅くなります。 –

+0

それも私が思ったことです。私の状況に対処する適切な方法は何ですか? – Herbstein

+0

例を追加しようとします。 –

答えて

2

、あなたはそうする.AsParallel()を使用することができます。

これは、プロセッサの機能に応じて、同時に生成されるスレッドの数をインテリジェントに制限します。ただし、アイテムごとの作業量が比較的多い場合にのみ使用するようにしてください。

ここでは例です:

using System; 
using System.Linq; 
using System.Threading; 

namespace Demo 
{ 
    class Program 
    { 
     static void Main() 
     { 
      var numbersToProcess = Enumerable.Range(1, 1000); 

      numbersToProcess.AsParallel().ForAll(doStuff); 
     } 

     static void doStuff(int value) 
     { 
      Console.WriteLine("Thread {0} is processing {1}", Thread.CurrentThread.ManagedThreadId, value); 
      Thread.Sleep(250); // Simulate compute-bound task. 
     } 
    } 
} 

別のアプローチは、各メソッド呼び出しのためのタスクを作成することですが、あなたは待つために作業を保存しない限り、すべてのスレッドが完了したとき、それは知っているより困難になりますそれらは完了します(ただし、スレッドプールの使用量は、スレッドの数が大きくなりすぎていないことが保証されます):

using System; 
using System.Linq; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Demo 
{ 
    class Program 
    { 
     static void Main() 
     { 
      var numbersToProcess = Enumerable.Range(1, 1000); 

      foreach (int number in numbersToProcess) 
      { 
       int n = number; 
       Task.Run(() => doStuff(n)); 
      } 

      Console.ReadLine(); 
     } 

     static void doStuff(int value) 
     { 
      Console.WriteLine("Thread {0} is processing {1}", Thread.CurrentThread.ManagedThreadId, value); 
      Thread.Sleep(250); // Simulate compute-bound task. 
     } 
    } 
} 

NOTEにこのアプローチを私は、作成されたスレッドの暴走数のリスクを実行しませんfをdoStuff()に電話すると非常に時間がかかります。 Thread.Sleep(250)Thread.Sleep(100000)に変更してプログラムを実行すると、多数のスレッドが作成されていることがわかります。

しかし、最も良い方法はthe DataFlow TPLです。

+0

処理する座標の配列を作成することができますが、それはひどく非効率的です。これを行うためのThreadPoolのようなメソッドはありませんか? – Herbstein

+0

@Herbstein私は2番目の例のように 'Tasks'を使うことができますが、私が指摘しているように、最後のタスクがいつ完了したかを知ることはもっと厄介です。しかし、おそらくDataFlow TPLに目を向けたいと思うように聞こえます。それには大きな学習曲線がありますが、あなたが望むものに最も適している可能性があります。 –

+0

助けてくれてありがとう!私はおそらく 'Task'のみのバージョンを実装し、もう少し時間があればDataFlow TPLを調べます。再度、感謝します。 – Herbstein

1

小さなボディパターンにはParallelループを使用します。 https://msdn.microsoft.com/en-us/library/dd560853(v=vs.110).aspx

Parallel.Forループは、小さな体を持っている は、そのようなVisual BasicでループのためにC#でのforループと同様に、同等のシーケンシャルループよりもゆっくりと行うことがあります。パフォーマンスの低下は、データの分割に伴うオーバーヘッドと各ループ反復でデリゲートを呼び出すコストによって引き起こされます。このようなシナリオに対処するために、PartitionerクラスにはPartitioner.Createメソッドが用意されています。このメソッドを使用すると、デリゲート本体のシーケンシャルループを提供できるため、デリゲートは繰り返しごとに1回ではなく、パーティションごとに1回のみ呼び出されます。

小体パターン用の並列ループパターンは、本質的に列挙型を分割し、プロセッサの数に応じて複数のスレッドでループを実行します。各スレッドは、作業する独自のパーティショングループを持ちます。

このパターンは、必要以上に多くのスレッドを作成するオーバーヘッドを避けるため、このシナリオでは通常のパラレルループより優れています(パフォーマンスが向上します)。 CPUコアより

使用スレッドよりは、単に計算指向タスクのプロセッサコアよりも多くのスレッドが悪いさを有する、処理一般に

+1

これがスレッドとどう関係するか説明してください。 –

+0

リンクのみの回答は欲求不満です。リンクが死ぬと答えはもはや価値がありません。リンクの関連情報をあなたの答えに引用ブロックで含めてください。 – Phaeze

+0

@Devlin小体パターンのためのParallel Loopは基本的に列挙型を分割し、プロセッサの数に応じて複数のスレッドでループを実行します。各スレッドは独自のパーティショングループを持ちます。 –

関連する問題