threaded for loop reaching values it's not supposed to reach

Viewed 50

I'm messing around with multithreading in c++ and here is my code:

#include <iostream>
#include <vector>
#include <string>
#include <thread>

void read(int i);

bool isThreadEnabled;
std::thread threads[100];

int main()
{
    isThreadEnabled = true; // I change this to compare the threaded vs non threaded method
    if (isThreadEnabled)
    {
        for (int i = 0;i < 100;i++) //this for loop is what I'm confused about
        {
            threads[i] = std::thread(read,i);
        }

        for (int i = 0; i < 100; i++)
        {
            threads[i].join();
        }
    }
    else
    {
        for (int i = 0; i < 100; i++)
        {
            read(i);
        }
    }
}

void read(int i)
{
    int w = 0;
    while (true) // wasting cpu cycles to actually see the difference between the threaded and non threaded
    {
        ++w;
        if (w == 100000000) break;
    }
    std::cout << i << std::endl;
}

in the for loop that uses threads the console prints values in a random order ex(5,40,26...) which is expected and totally fine since threads don't run in the same order as they were initiated...

but what confuses me is that the values printed are sometimes more than the maximum value that int i can reach (which is 100), values like 8000,2032,274... are also printed to the console even though i will never reach that number, I don't understand why ?

1 Answers

This line:

std::cout << i << std::endl;

is actually equivalent to

std::cout << i;
std::cout << std::endl;

And thus while thread safe (meaning there's no undefined behaviour), the order of execution is undefined. Given two threads the following execution is possible:

T20: std::cout << 20
T32: std::cout << 32
T20: std::cout << std::endl
T32: std::cout << std::endl

which results in 2032 in console (glued numbers) and an empty line.

The simplest (not necessarily the best) fix for that is to wrap this line with a shared mutex:

{
    std::lock_guard lg { mutex };
    std::cout << i << std::endl;
}

(the brackets for a separate scope are not needed if the std::cout << i << std::endl; is the last line in the function)

Related