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

我有一个对象列表,我想循环遍历该列表并启动一个新线程,传入当前对象。

我写了一个我认为应该做的事情的例子,但它不起作用。 具体来说,似乎线程在每次迭代时都被覆盖。 这对我来说真的没有意义,因为我每次都在创建一个新的Thread对象。

这是我写的测试代码

class Program { static void Main(string[] args) { TestClass t = new TestClass(); t.ThreadingMethod(); } } class TestClass { public void ThreadingMethod() { var myList = new List { 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关于这个确切主题的post: 关闭循环变量被认为是有害的

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

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

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

我想补充一点,如果您使用的是.NET 4,您可能需要查看Task Parallel Library来解决这类问题。 特别是对于这种情况,看看Parallel.ForEach() 。

如果序列无关紧要

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

写一个简单的Parallel.ForEach循环

我更喜欢这种方式:

 public void ThreadingMethod() { var myList = new List { new MyClass("test1"), new MyClass("test2") }; Parallel.ForEach(myList, new ParallelOptions() { MaxDegreeOfParallelism = 100 }, (myObj, i, j) => { MyMethod(myObj); }); } 

虽未测试….