web-dev-qa-db-ja.com

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));

しかし、どのスレッドも開始されませんでした。

スレッドがどのように機能するかについて誤解しているだけだと思います。誰かが私を正しい方向に向けて、私が間違っていることを教えてもらえますか?

23
Kris Harper

これは、間違ったスコープで変数を閉じようとしているためです。ここでの解決策は、foreachループで一時変数を使用することです。

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

詳細については、この正確な主題に関するEric Lippertの投稿を読むことをお勧めします: 有害と見なされるループ変数を閉じる

43
Reed Copsey

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

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

リードの答えに同意する(+1)。

.NET 4を使用している場合は、この種の問題を解決するためにタスク並列ライブラリを確認することをお勧めします。特にこのケースでは、 Parallel.ForEach() をご覧ください。

2
Eric J.

順序が問題ではない場合

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

単純なParallel.ForEachループを記述

1
Pranay Rana

私はこの方法を好む:

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);
         });

}

テストされていません...

1
SolidSnake