ThreadPool异常

Ole*_*leg 4 .net c# multithreading threadpool

我需要一次使用60个线程(并行),为此我使用ThreadPool.我有例外:

temp = 1;
for (int j = 0; j < temp; j++) {
   ThreadPool.QueueUserWorkItem(delegate(object notUsed) {
      RequestToRajons(rajs_ip[j].ToString(),rajs_name[j].ToString(), sql_str, base_str, saveFileDialog1,j); 
   });
}
Run Code Online (Sandbox Code Playgroud)

它给了我一个例外j=1(数组超出范围).但我有一个contidion!如果我在步骤中使用断点,我没有例外.

Mar*_*ell 12

这是经典的/捕获问题,因为你正在"捕获" j,而且只有一个j.所有线程都使用相同的j变量进行处理; 他们看到的是不确定的,但最后几个线程很可能会看到循环的退出值,即一个太多.

代替:

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); });
}
Run Code Online (Sandbox Code Playgroud)

这听起来很愚蠢,但你现在有一个jper循环迭代,因为捕获的范围取决于变量的声明范围.这里,在循环内部j定义.在循环中,变量在技术上定义在循环之外.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
}
Run Code Online (Sandbox Code Playgroud)

这是"捕获的变量"和匿名方法如何工作的扩展版本,过度简化的版本; 首先,编译器为您执行此操作:

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
}
Run Code Online (Sandbox Code Playgroud)

并且您的方法变为(因为j技术上定义在循环之外):

var ctx = new CaptureContext();
for (ctx.j = 0; ctx.j < temp; ctx.j++) {
    ThreadPool.QueueUserWorkItem(ctx.SomeMethod);
}
Run Code Online (Sandbox Code Playgroud)

现在; 你能看到只有一个"捕获"对象ctx.j吗?我们在随机点上使用它的时间不一定是我们认为的那样吗?该修复程序将其重写为:

for (int loopIndex = 0; loopIndex < temp; loopIndex++) {
    var ctx = new CaptureContext();
    ctx.j = loopIndex;
    ThreadPool.QueueUserWorkItem(ctx.SomeMethod);
}
Run Code Online (Sandbox Code Playgroud)

在这里,你能看到每次迭代都有一个"捕获"对象,这是因为它j循环声明的吗?"什么是新的捕获上下文"取决于捕获的变量的范围.