我有一个对象列表,我想循环遍历该列表并启动一个新线程,传入当前对象。
我写了一个我认为应该做的事情的例子,但它不起作用。具体来说,似乎线程在每次迭代时都会被覆盖。这对我来说真的没有意义,因为我每次都在创建一个新的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));
但没有一个线程开始。
我认为我对线程应如何工作存在误解。有人能指出我正确的方向并告诉我我做错了吗?
答案 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)
答案 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);
});
}
虽未测试....