【问题标题】:ThreadPool Exception线程池异常
【发布时间】:2011-12-22 12:05:58
【问题描述】:

我需要一次(并行)使用 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); 
   });
}

j=1(数组超出范围)给了我一个例外。但我有条件! 如果我在 step 中使用断点,我没有遇到异常。

【问题讨论】:

    标签: c# .net multithreading threadpool


    【解决方案1】:

    这是经典的 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 被声明为在循环内? “什么是新的捕获上下文”取决于所捕获变量的范围

    【讨论】:

    • 乍一看和我写的一样。但它工作 - 谢谢。能否请您详细解释一下。
    • @Oleg 我已经添加了几行试图解释这一点 - 还不清楚吗?
    • @Oleg 扩展了更多细节
    猜你喜欢
    • 2010-10-11
    • 2021-04-11
    • 1970-01-01
    • 2013-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-07
    • 1970-01-01
    相关资源
    最近更新 更多