这是经典的 for/capture 问题,因为您正在“捕获”j,而只有一个j。您所有的线程都在使用相同的j 变量进行处理;他们看到的 值 是不确定的,但最后几个线程很可能会看到循环的 exit 值,即太多了。
改为:
for (int loopIndex = 0; loopIndex < temp; loopIndex++)
{
int j = loopIndex;
// the following line has not changed at all
ThreadPool.QueueUserWorkItem(delegate(object notUsed) { RequestToRajons(rajs_ip[j].ToString(),rajs_name[j].ToString(), sql_str, base_str, saveFileDialog1,j); });
}
这听起来很傻,但现在每个循环迭代都有一个j,因为捕获 的范围取决于变量的声明范围。在这里,j 被定义在 inside 循环中。在for 循环中,变量在技术上定义在在循环之外。
另一种方法是使用线程池的参数:
for(int loopIndex = 0; loopIndex < temp; loopIndex++)
{
ThreadPool.QueueUserWorkItem(delegate(object ctx) {
int j = (int) ctx;
// stuff involving j
}, loopIndex); // <=== see we're passing it in, rather than capturing
}
这是“捕获变量”和匿名方法如何工作的扩展版本,过度简化的版本;首先,编译器会为您执行此操作:
class CaptureContext { // <== the real name is gibberish
public int j; // yes it is a field; has to be, so `ref` and `struct` etc work
public void SomeMethod(object notUsed) {
RequestToRajons(rajs_ip[j].ToString(),rajs_name[j].ToString(), sql_str, base_str, saveFileDialog1,j);
}
// it might also be capturing "this"; I can't tell from your example
}
你的方法变成了(因为j是技术上在循环外定义的):
var ctx = new CaptureContext();
for (ctx.j = 0; ctx.j < temp; ctx.j++) {
ThreadPool.QueueUserWorkItem(ctx.SomeMethod);
}
现在;您能看到只有一个“捕获”对象,并且我们在ctx.j 不一定是我们认为的那样的时间点随机使用它吗?该修复将其重写为:
for (int loopIndex = 0; loopIndex < temp; loopIndex++) {
var ctx = new CaptureContext();
ctx.j = loopIndex;
ThreadPool.QueueUserWorkItem(ctx.SomeMethod);
}
这里,你能看到有一个“捕获”对象每次迭代,这是因为j 被声明为在循环内? “什么是新的捕获上下文”取决于所捕获变量的范围。