0

I am not understanding how my program is not executing correctly and why I have to use std::this_thread::yield() to make it work.

This code does not print "hello" 1000 times.

#include <iostream>
#include <thread>
bool start = false;


void wait_print_hello()
{
    while (!start)
    {
    }

    for (int i = 0; i < 1000; i++)
    {
        std::cout << "hello" << std::endl;
    }
}


void print_one_thousand()
{
    for (int i = 0; i < 1000; i++)
    {
        std::cout << i + 1 << std::endl;
    }
    start = true;
}


int main()
{
    std::thread first(print_one_thousand);
    std::thread second(wait_print_hello);

    first.join();
    second.join();

    return 0;
}

This code prints "hello" 1000 times

#include <iostream>
#include <thread>
bool start = false;


void wait_print_hello()
{
    while (!start)
    {
        std::this_thread::yield();
    }

    for (int i = 0; i < 1000; i++)
    {
        std::cout << "hello" << std::endl;
    }
}


void print_one_thousand()
{
    for (int i = 0; i < 1000; i++)
    {
        std::cout << i + 1 << std::endl;
    }
    start = true;
}


int main()
{
    std::thread first(print_one_thousand);
    std::thread second(wait_print_hello);

    first.join();
    second.join();

    return 0;
}

What baffles me even more is that this code prints "hello" 1000 times for me also

#include <iostream>
#include <thread>
bool start = false;


void wait_print_hello()
{
    while (!start)
    {
        std::cout << start << std::endl;
    }

    for (int i = 0; i < 1000; i++)
    {
        std::cout << "hello" << std::endl;
    }
}


void print_one_thousand()
{
    for (int i = 0; i < 1000; i++)
    {
        std::cout << i + 1 << std::endl;
    }
    start = true;
}


int main()
{
    std::thread first(print_one_thousand);
    std::thread second(wait_print_hello);

    first.join();
    second.join();

    return 0;
}

Why does is it work like this?

Kyle C
  • 207
  • 1
  • 7
  • 1
    Make your start bool std::atomic or the program is *BROKEN* . – Zan Lynx Dec 22 '19 at 04:21
  • 1
    All three programs contain a data race on `start`, and therefore exhibit undefined behavior. "Seems to work" is one possible manifestation of undefined behavior; "spins in infinite loop" is another. – Igor Tandetnik Dec 22 '19 at 04:22
  • So I have made my start bool std::atomic and all three programs work as expected, but I am still curious about when a person should use std::this_thread::yield()? – Kyle C Dec 22 '19 at 04:44
  • 1
    @KyleCurry `std::this_thread::yield()` is just a hint for the OS that the current thread doesn't need to do any work right now, so that the OS can favor other threads when scheduling processor time. – walnut Dec 22 '19 at 04:46
  • 1
    See [C++ if one thread writes toggles a bool once done, is it safe to read that bool in a loop in a single other thread?](https://stackoverflow.com/questions/41212494/c-if-one-thread-writes-toggles-a-bool-once-done-is-it-safe-to-read-that-bool) or [In a multi-threaded C++ app, do I need a mutex to protect a simple boolean?](https://stackoverflow.com/questions/222916/in-a-multi-threaded-c-app-do-i-need-a-mutex-to-protect-a-simple-boolean) or [Can I read a bool variable in a thread without mutex?](https://stackoverflow.com/questions/47867789/can-i-read-a-bool-variable-in-a-thread-without-mutex) – walnut Dec 22 '19 at 04:48
  • 1
    @KyleCurry: There is no answer to this. `yield` merely *happens* to work, on this CPU, in this C++ implementation, and in this case. It may not work tomorrow on the same CPU, implementation, and code. As far as C++ is concerned, the presence or absence of `yield` has no effect on whether your program has a data race. It's a data race and therefore UB. Basically, your question is moot because the code it's based on has no well-defined behavior. – Nicol Bolas Dec 22 '19 at 04:49
  • 1
    It isn't the call to `yield` specifically, but including a function call in the loop causes the compiler to check the value of `start` every iteration (since the called function may have modified the global variable). In the loop with an empty body, the compiler doesn't see anything to change `start` and may create an infinite loop. – 1201ProgramAlarm Dec 22 '19 at 04:57

0 Answers0