4

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.

0

1 Answer 1

12

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.

Sign up to request clarification or add additional context in comments.

3 Comments

At first look the same thing that I wrote. But it work - thanks. Could you explain me more details please.
@Oleg I already added a few lines trying to explain that - is it still unclear?
@Oleg expanded with a lot more detail

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.