我在循环中创建线程,我需要在线程启动方法中传递循环计数器(就像创建线程时一样)。
for (int i = 0; i < ListSize; i++)
{
thread = new Thread(() =>
Helper.GetEndToEndRequestProcessTime(EmailList[i], reqResultData, i, ListSize));
workerThreads.Add(thread);
thread.Start();
}
在上面的示例中,i
是变量I作为GetEndToEndRequestProcessTime()
中的参数。但问题是当实际线程的起始值i
正在发生变化时,我在GetEndToEndRequestProcessTime()
得到了错误的结果。
如何确保在线程启动时传递给GetEndToEndRequestProcessTime()
的值与创建线程时提供的值相同。
答案 0 :(得分:5)
每个新手必须要问的经典闭包和捕获变量问题。
for (int i = 0; i < ListSize; i++)
{
var j = i;
thread = new Thread(() => Helper.GetEndToEndRequestProcessTime(EmailList[j], reqResultData, j, ListSize));
workerThreads.Add(thread);
thread.Start();
}
更多信息:http://csharpindepth.com/articles/chapter5/closures.aspx
答案 1 :(得分:2)
如上所述,这里的问题是闭包问题。
这意味着因为您只为i
分配了一个变量,当您通过在new Thread(() => ...)
中引用它来捕获它时,您实际上正在复制变量的地址。因此,当变量为下一个循环递增时,第一个thread
中正在进行的操作将获得更新的值,因为它都指向i
的单个值。
解决此问题的方法是将变量复制到新变量中,然后捕获新变量。通过在循环中声明它,将为每次迭代创建一个新的迭代,因为循环的内部范围不在迭代之间共享。
注意类似的问题适用于.NET 4.0及更早版本的foreach
循环中的捕获变量,但在4.5及更高版本中已修复。
答案 2 :(得分:1)
如何在线程启动值时确保 传递给GetEndToEndRequestProcessTime()与提供时相同 线程是否已创建?
答案:通过创建一个本地副本,该副本将记住当执行的命令被赋予此任务/线程然后传递此本地副本时i
的值是什么。 / p>
确切问题:当您启动任务/线程时,i
的值已更改,因此您的执行意外。