Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreadPool Exception

I need to use 60 threads in one time(parallelly) and for it I use ThreadPool. I have exception here:

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); 
   });
}

It's gives me exception that j=1(array out of range). But I have a contidion! If I use a breakpoint with step, I haven't got an exception.

like image 572
Oleg Avatar asked Dec 16 '22 05:12

Oleg


1 Answers

This is the classic for/capture issue, because you are "capturing" j, and there is only one j. All your threads are processing using the same j variable; the value they see is indeterminate, but the last several threads will most likely see the exit value of the loop, i.e. one too many.

Instead:

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); });
}

it sounds silly, but you now have a j per loop iteration, because the scope of the capture depends on the declaration scope of the variable. Here, j is defined inside the loop. In the for loop, the variable is technically defined outside the loop.


Another way to do this is to use the parameter to the thread-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
}

Here's an expanded version of how "captured variables" and anonymous methods work, the over-simplified version; firstly, the compiler does this for you:

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
}

and your method becomes (because j is technically defined outside the loop):

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

now; can you see that there is only one "capture" object, and that we're using it at random points in times where ctx.j is not necessarily what we thought it was? The fix rewrites that as:

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

here, can you see there is a "capture" object per iteration, which is because the j is declared inside the loop? The "what is a new capture context" depends on the scope of the variables that are captured.

like image 103
Marc Gravell Avatar answered Dec 18 '22 18:12

Marc Gravell