Home > Back-end >  Why is the for loop and the hard coded yielding different results?
Why is the for loop and the hard coded yielding different results?

Time:12-23

I have created a program in C that doesn't work when the thread vectors' components are created with a for loop and does when they are created hard coded. This is the hard coded example:

std::vector<std::thread> ThreadVector;
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[0], 0);})));
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[1],1);})));
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[2],2);})));
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[3],3);})));
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[4],4);})));

for(auto& t:ThreadVector)
        t.join();

maxLast is an integer that is the value of the double char array. the vals variable is a tripple char array, think a vector of strings within a vector. and the last value is a number to send through the function. The cl.run function basically just takes an array of char arrays, writes to a file the maxLast variable then on the next line the next string from the array. The name of the file is called trying then the added number then .txt the function is shown below:

void dir::run(int nums, char *vals[nums], int count){
    std::cout<<"got started "<<nums<<" "<<count<<std::endl;
    std::string filestr = "trying" std::to_string(count) ".txt";
    for(int i = 0; i < nums; i  ){
        writeLine(std::to_string(nums), filestr);
        writeLine(std::string(vals[i]), filestr);
    }
}

void dir::writeLine(std::string w, std::string p){
    FILE* stream = fopen(p.c_str(), "a ");  
    fputs(w.c_str(), stream);
    fputs("\n", stream);
    fclose(stream);

}

When the hardcoded into the ThreadVector variable as shown above is run, it works perfectly. However, if I were to run it the following way, there would be a file called trying5.txt there would also be strings I never had put into the char array printed onto the file. There would also be some threads that received the same i value.

int i;
int ques=5;
for(i = 0; i < ques; i  ){
    printf("running %d\n", i);
    ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[i],i);})));
}
for(auto& t:ThreadVector)
        t.join();

which prints on one of the runs into the terminal (cleaned up a bit):

running 0
running 1
running 2
running 3
running 4
got started 30 2
got started 30 2
got started 30 4
got started 30 3
got started 30 4

Which is obviously not what is supposed to happen, and as mentioned sometimes the terminal would print got started 30 5 and I would end up with a file named trying5.txt. I have also tried push_back instead of emplace_back.

CodePudding user response:

std::thread([&]

[&] means that all objects captured by the closure get captured by reference.

cl.run(maxLast, vals[i],i)

i was captured by reference. i was the parent execution thread's loop variable, that gets incremented on every iteration of the loop.

C gives you no guarantees, whatsoever, when each execution thread executes anything, in relation to anything in the parent execution thread, unless explicit synchronization takes place.

No such synchronization occurs here. The parent execution thread might've already incremented i by the time the new execution thread evaluates this val[i], as well as the discrete i parameter to the function call; it already ended the current iteration of the loop and moved to the next one. Or even iterated several times. Or the loop may be completely over in the parent execution thread, and i is now at its final value.

got started 30 2
got started 30 2
got started 30 4
got started 30 3
got started 30 4

You see exactly this result, here. Each execution thread gets around to evaluating its i after the parent execution thread iterated, and incremented i, an unpredictable number of times. Some of the new execution threads even managed to evaluate i at the same time, resulting in the same observed value of i.

This is what capturing objects "by reference means". Use [=] to capture by value instead. This means that each execution thread sees the value of each captured object at the time the execution thread was created.

  • Related