Unexpected result when accessing main thread variable in loop created child threads

Issue

I am writing a program that reads a large amount of dynamic data. I would like to use multi-threading to speed this process up. I understand that it is up to the operating system on how to handle created threads (thread priority, etc), which I believe is the reason for my unexpected results, however, I still do not know a solution.

Code:

int multiplier = rowCount / 20;

Debug.WriteLine("Row count is " + rowCount + "...");
Debug.WriteLine("Using 20 threads to complete job...");
Debug.WriteLine("Using " + multiplier + " as multiplier...");

for (int i = 1; i <= 20; i++) {
    new Thread(() => {
        int startRow = ((multiplier * i) - multiplier) + 1;
        int endRow = multiplier * i;
        
        if (i == 20) {
            endRow = rowCount;
        }
    
        Debug.WriteLine("    [THREAD " + i + "] start row: " + startRow + ", end row : " + endRow);

        for (int row = startRow; row <= endRow; row++) {
            for (int column = 1; column <= columnCount; column++) {
                //...data is read here
            }
        }
    }).Start();
}

Actual result (it appears that my issue is the child thread not "reading" the ‘i’ variable "correctly", which makes sense considering how threads work, I just do not know how to fix it):

Row count is 2209...
Using 20 threads to complete job...
Using 110 as multiplier...
    [THREAD 4] start row: 331, end row : 440
    [THREAD 4] start row: 331, end row : 440
    [THREAD 5] start row: 441, end row : 550
    [THREAD 5] start row: 441, end row : 550
    [THREAD 6] start row: 551, end row : 660
    [THREAD 7] start row: 661, end row : 770
    [THREAD 9] start row: 881, end row : 990
    [THREAD 9] start row: 881, end row : 990
    [THREAD 11] start row: 1101, end row : 1210
    [THREAD 11] start row: 1101, end row : 1210
    [THREAD 12] start row: 1211, end row : 1320
    [THREAD 14] start row: 1431, end row : 1540
    [THREAD 15] start row: 1541, end row : 1650
    [THREAD 15] start row: 1541, end row : 1650
    [THREAD 16] start row: 1651, end row : 1760
    [THREAD 17] start row: 1761, end row : 1870
    [THREAD 19] start row: 1981, end row : 2090
    [THREAD 20] start row: 2091, end row : 2209
    [THREAD 20] start row: 2091, end row : 2209
    [THREAD 21] start row: 2201, end row : 2310

Expected result (this is the result of simply not using threads, meaning commenting out the lambda expression):

Row count is 2209...
Using 20 threads to complete job...
Using 110 as multiplier...
    [THREAD 1] start row: 1, end row : 110
    [THREAD 2] start row: 111, end row : 220
    [THREAD 3] start row: 221, end row : 330
    [THREAD 4] start row: 331, end row : 440
    [THREAD 5] start row: 441, end row : 550
    [THREAD 6] start row: 551, end row : 660
    [THREAD 7] start row: 661, end row : 770
    [THREAD 8] start row: 771, end row : 880
    [THREAD 9] start row: 881, end row : 990
    [THREAD 10] start row: 991, end row : 1100
    [THREAD 11] start row: 1101, end row : 1210
    [THREAD 12] start row: 1211, end row : 1320
    [THREAD 13] start row: 1321, end row : 1430
    [THREAD 14] start row: 1431, end row : 1540
    [THREAD 15] start row: 1541, end row : 1650
    [THREAD 16] start row: 1651, end row : 1760
    [THREAD 17] start row: 1761, end row : 1870
    [THREAD 18] start row: 1871, end row : 1980
    [THREAD 19] start row: 1981, end row : 2090
    [THREAD 20] start row: 2091, end row : 2209

Solution

Try moving the variables that can be calculated outside of the thread to avoid reading the shared variable i in the thread. The threads are started without care of the surrounding loop which increments i.

int multiplier = rowCount / 20;

Debug.WriteLine("Row count is " + rowCount + "...");
Debug.WriteLine("Using 20 threads to complete job...");
Debug.WriteLine("Using " + multiplier + " as multiplier...");

for (int i = 1; i <= 20; i++) {
    int startRow = ((multiplier * i) - multiplier) + 1;
    int endRow = multiplier * i;
    if (i == 20) {
       endRow = rowCount;
    }
    int nThread = i;
    new Thread(() => {
        Debug.WriteLine("    [THREAD " + nThread + "] start row: " + startRow + ", end row : " + endRow);

        for (int row = startRow; row <= endRow; row++) {
            for (int column = 1; column <= columnCount; column++) {
                //...data is read here
            }
        }
    }).Start();
}

It is apparent in the first log you shared, for example when thread 1 and 2 starts, i is already at 4.

Answered By – user19902261

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published