在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); }); }
虽未测试….