2012-02-23 11 views
21

私はオブジェクトのリストを持っています。そのリストをループして新しいスレッドを開始し、現在のオブジェクトを渡したいと思います。foreachループで新しいスレッドを開始する

私はこれを行うべきだと思った例を書いたが、うまくいかない。具体的には、各繰り返しでスレッドが上書きされているようです。私は新しいThreadオブジェクトを毎回作成しているので、これは私にとっては意味がありません。

これは私が私のマシン上で

class Program 
{ 
    static void Main(string[] args) 
    { 
     TestClass t = new TestClass(); 
     t.ThreadingMethod(); 
    } 
} 

class TestClass 
{ 
    public void ThreadingMethod() 
    { 
     var myList = new List<MyClass> { new MyClass("test1"), new MyClass("test2") }; 

     foreach(MyClass myObj in myList) 
     { 
      Thread myThread = new Thread(() => this.MyMethod(myObj)); 
      myThread.Start(); 
     } 
    } 

    public void MyMethod(MyClass myObj) { Console.WriteLine(myObj.prop1); } 
} 

class MyClass 
{ 
    public string prop1 { get; set; } 

    public MyClass(string input) { this.prop1 = input; } 
} 

に出力を書いたテストコードでは

test2 
test2 

ですが、私は、スレッドラインを変更しようとしたことが

test1 
test2 

ことが期待しました〜

ThreadPool.QueueUserWorkItem(x => this.MyMethod(myObj)); 

しかし、開始されたスレッドはありません。

私はスレッドの動作方法について誤解していると思います。誰かが私を正しい方向に向けることができ、私が間違っていることを教えてもらえますか?

+1

あなたの人生になります.Net 3.5で導入されたParallel Extensions Libraryをチェックするとずっと簡単です。開始する場所は次のとおりです。http://msdn.microsoft.com/en-us/library/dd460693%28VS.100%29.aspx – DOK

+0

http://www.albahari.com/threading/ –

答えて

38

これは、間違った範囲で変数をクローズしているためです。ここでの解決策は、あなたのforeachループで一時的に使用することです。詳細については

foreach(MyClass myObj in myList) 
    { 
     MyClass tmp = myObj; // Make temporary 
     Thread myThread = new Thread(() => this.MyMethod(tmp)); 
     myThread.Start(); 
    } 

、私はこの正確なテーマに関するエリックリペットの記事を読んでお勧めします。Closing over the loop variable considered harmful

+1

あなたは私にそれを打つ! –

+0

これは迅速な対応でした。ワオ。 – BlueM

+1

ガー、私は実際に答えているので、私は実際に[この質問](http://stackoverflow.com/q/8898925/817630)を読んでいるので、自分自身を蹴っています。私もEricのブログ記事を見ました。私が自分自身を覚えていたはずのことを指摘してくれてありがとう。 –

3

問題は、最新の値を使用していることですあなたのクロージャの内側のオブジェクトの。したがって、スレッドの各呼び出しは同じ値を調べています。この値を回避するには、値をローカル変数にコピーします。

foreach(MyClass myObj in myList) 
{ 
    MyClass localCopy = myObj; 
    Thread myThread = new Thread(() => this.MyMethod(localCopy)); 
    myThread.Start(); 
} 
2

Reedの回答(+1)に同意します。

.NET 4の場合は、このクラスの問題を解決するためにTask Parallel Libraryを参照することをお勧めします。具体的には、Parallel.ForEach()をご覧ください。

+1

私はParallel.ForEachが好きですが、OP自体がブロッキング方法であることを認識していますが、OPは「火と忘れ」なので、機能的に違いがあります。 –

1

シーケンスではない場合、私はこの方法を好む

Parallel.ForEach(myList, obj => this.MyMethod(obj)); 

Write a Simple Parallel.ForEach Loop

+0

Parallel.ForEach自体は、OPが「火と忘れ」のブロック方法です。そのため、機能的に違いがあります。この場合、これは適切ではないかもしれません。少なくとも、タスク自体の中に入れてはいけません。 –

1

のために行くよりもかまい:しかしテストされていません

public void ThreadingMethod() 
{ 
    var myList = new List<MyClass> { new MyClass("test1"), new MyClass("test2") }; 


Parallel.ForEach(myList, new ParallelOptions() { MaxDegreeOfParallelism = 100 }, 
     (myObj, i, j) => 
     { 
      MyMethod(myObj); 
     }); 

} 

....

+0

Parallel.ForEachはOPのメソッドとは異なる機能的動作をしています。元のコードは動作が「消し忘れています」があり、操作が完了するまで呼び出しスレッドがブロックされます。また、特定の理由がない限り、MaxDegreeOfParallelismの設定はお勧めしません。 –

+0

@ReedCopsey - あなたはそれを前提としていると思います。 Parallel.ForEachを使用することは、アプリケーションにPROPERスレッディングを実装するための優先メソッドです(特に、同時に処理するアイテムがたくさんあり、単純なThread()を使用するのに十分なリソースがない場合)。あなたが提案した方法で1000のスレッドを使用する場合は非常に実装が貧弱です。 – SolidSnake

+0

Parallel.ForEachは必ずしも望ましい方法ではありません。それは良い選択ですが、多くのうちの1つです。 TasksまたはThreadPoolを使用するのは、自分自身でスレッドを管理するよりも良い選択肢ですが、それでもスレッドを直接使用することが望ましい場合もあります。 OPのコードには、Parallel.ForEachが好ましいオプションであることを示す十分な情報がありません。 (しかし、私はほとんど常に手動で管理されたスレッドを介して長時間実行されるタスクを選択することができます) –

関連する問題