Question

Example code that produces the problem:

import std.stdio, core.thread;
void main() {
    ThreadGroup tg = new ThreadGroup();
    foreach (int i; 1 .. 5)
        tg.create( () => writeln(i) );
    tg.joinAll();
}

Sample output:

3
5
5
5
5

(The expected output was the integers 1 through 5.)

I don't get why this is happening -- i is not a reference type, nor is there a reference to it being used in the delegate, so why does each thread use the value of i as whatever value it happens to have when the thread is scheduled, instead of the presumably pass-by-value value it's given?

I've made a handful of lame attempts like this, but nothing's been successful:

foreach (int i; 1 .. 5) {
    scope j = i;
    tg.create( () => writeln(j) );
}

I'm curious as to why this doesn't work, either. Is it not declaring a fresh j each time? Why does each thread refer to the same j (whose value is usually 5 by the time the threads get scheduled)?

Was it helpful?

Solution

so why does each thread use the value of i as whatever value it happens to have when the thread is scheduled, instead of the presumably pass-by-value value it's given?

It's pass-by-value as far as the loop body goes, however that does not apply to the threads created in it. The threads will still refer to i by its address.

To fix this problem, you need to create a closure inside the loop:

import std.stdio, core.thread;
void main() {
    ThreadGroup tg = new ThreadGroup();
    foreach (int i; 1 .. 5)
        (i =>
            tg.create( () => writeln(i) )
        )(i);
    tg.joinAll();
}

The lambda parameter will be stored in the closure, giving each thread its own copy.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top