オブジェクトのリストがあり、そのリストをループして新しいスレッドを開始し、現在のオブジェクトを渡したいのですが。
私はこれを行うべきだと思った例を書きましたが、うまくいきません。具体的には、各反復でスレッドが上書きされているようです。毎回新しい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));
しかし、どのスレッドも開始されませんでした。
スレッドがどのように機能するかについて誤解しているだけだと思います。誰かが私を正しい方向に向けて、私が間違っていることを教えてもらえますか?
これは、間違ったスコープで変数を閉じようとしているためです。ここでの解決策は、foreachループで一時変数を使用することです。
foreach(MyClass myObj in myList)
{
MyClass tmp = myObj; // Make temporary
Thread myThread = new Thread(() => this.MyMethod(tmp));
myThread.Start();
}
詳細については、この正確な主題に関するEric Lippertの投稿を読むことをお勧めします: 有害と見なされるループ変数を閉じる
問題は、クロージャー内でオブジェクトの最新の値を使用していることです。したがって、スレッドの各呼び出しは同じ値を調べています。これを回避するには、値をローカル変数にコピーします。
foreach(MyClass myObj in myList)
{
MyClass localCopy = myObj;
Thread myThread = new Thread(() => this.MyMethod(localCopy));
myThread.Start();
}
リードの答えに同意する(+1)。
.NET 4を使用している場合は、この種の問題を解決するためにタスク並列ライブラリを確認することをお勧めします。特にこのケースでは、 Parallel.ForEach() をご覧ください。
私はこの方法を好む:
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);
});
}
テストされていません...