在foreach循环中启动一个新线程

时间:2012-02-23 18:00:16

标签: c# .net multithreading 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));

但没有一个线程开始。

我认为我对线程应如何工作存在误解。有人能指出我正确的方向并告诉我我做错了吗?

5 个答案:

答案 0 :(得分:42)

这是因为您正在关闭错误范围内的变量。这里的解决方案是在foreach循环中使用临时:

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

有关详细信息,我建议您阅读Eric Lippert关于此主题的帖子:Closing over the loop variable considered harmful

答案 1 :(得分:3)

问题是您正在使用闭包内对象的最新值。因此,每次调用线程都会查看相同的值。要解决此问题,请将值复制到局部变量中:

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

答案 2 :(得分:2)

同意里德的回答(+1)。

我想补充一点,如果您使用的是.NET 4,您可能需要查看任务并行库以解决此类问题。特别针对这种情况,请查看Parallel.ForEach()

答案 3 :(得分:1)

如果序列无关紧要

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

Write a Simple Parallel.ForEach Loop

答案 4 :(得分: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);
         });

}

虽未测试....