我一直在玩线程和任务(.net 4)并注意到一些奇怪的行为,当你启动多个线程而不等待每个线程启动调用之间几毫秒。
运行时的以下示例不会输出我的预期:
1
2
1
2
但是只输出:
2
2
2
2
以下是我正在运行的代码。
public static void Main()
{
var items = new[] {"1", "2"};
foreach (var item in items)
{
var thread = new Thread(() => Print(item));
thread.Start();
//var task = Task.Factory.StartNew(() => Print(item));
}
}
static void Print(string something)
{
while (true)
{
Console.WriteLine(something);
Thread.Sleep(1000);
}
}
现在,当我在thread.Start()之后调用Thread.Sleep(50)时,输出只会按预期显示
1
2
1
2
我的问题是:
即。使用参数“1”启动第一个线程,使用参数“2”启动第二个线程,但是第一个线程的参数也变为“2”?这没有任何意义,特别是因为Print()方法参数是字符串的值类型。
答案 0 :(得分:5)
Google“访问修改后的关闭”。发生了什么事情是你的局部变量“item”在调用Print函数之前更改了它的值。解决方案是在循环范围内创建一个新变量并为其分配项目。
答案 1 :(得分:2)
在您创建的线程由于c#闭包而启动时,将评估该项目。强制项目评估的另一种方法是引入一个变量,以便闭包将包含它:
foreach (var item in items)
{
var closedItem = item;
var thread = new Thread(() => Print(closedItem));
thread.Start();
}
答案 2 :(得分:1)
你的问题不在于线程。你的问题在于封闭和foreach。你可以在这里阅读原因: http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx
当您使用线程的时间时,您也会对主线程的时间进行重新排序,因此有时循环将在新线程的print方法运行之前执行,有时也会在之后执行。
答案 3 :(得分:0)
向我们展示线程启动代码,你会发现你没有传递一个常量字符串而是一个引用变量,并且在调用那些Start方法之间你可能正在改变变量。