Исключение ThreadPool - PullRequest
       10

Исключение ThreadPool

4 голосов
/ 22 декабря 2011

Мне нужно использовать 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 (массив вне диапазона).Но у меня есть состояние!Если я использую точку останова с шагом, у меня не будет исключения.

1 Ответ

12 голосов
/ 22 декабря 2011

Это классическая проблема для / 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 для каждой итерации цикла, потому что область действия capture зависит от области объявления переменной.Здесь j определяется внутри цикла.В цикле for переменная технически определена вне цикла.


Другой способ сделать это - использовать параметр в потоке-pool:

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 объявлен внутри петля?«Что такое новый контекст захвата» зависит от scope перехваченных переменных.

...