Simple plain C ++ producer problem

I have really simple code for streaming consumer C ++ code, except I am typing "Hello World"

I want the following printable code to be:

"HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld"

condition_variable mcond; // the variable communicating events
mutex mmutex; // the locking mechanism

void hello()
{
    while (true) {
        unique_lock<mutex> lck{ mmutex }; // acquire mmutex
        mcond.wait(lck); /* do nothing */; // release lck and wait;
                                              // re-acquire lck upon wakeup
        cout << "Hello";

        lck.unlock();

   }
}

void world()
{
    while (true)
    {
        unique_lock<mutex> lck{ mmutex }; // protect operations

        cout << "World";
        mcond.notify_one();
    }
}

int main()
{
    thread t1{ hello };
    thread t2{ world };

    t1.join();
    t2.join();

    return 0;
}

      

What is the above code:

"WorldWorldWorldWorldWorldWorldWorldWorldWorldWorld Hello WorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorld Hello WorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldHelloWorldWorldWorldWorldWorld"

I suspect that the reason it doesn't print in sync is because the world () function releases the lock and immediately grabs it before hello () has a chance to grab it.

I have no idea how to solve this problem. How to properly configure thread synchronization?

+3


source to share


1 answer


Threads are inherently unsynchronized, so it is not known how long one thread will run before another gets a chance for a CPU

time.

If you need a strict order for your outlet, you will have to do so. One way is with a flag to tell each thread which word to print like this:

// avoid using global information
struct hello_world_control
{
    std::condition_variable cv;
    std::mutex mtx;
    bool do_hello = true; // start with "Hello"

    // make this atomic to avoid having to lock it
    std::atomic_bool done{false};
};

void hello(hello_world_control& ctrl)
{
    // setting ctrl.done = true; will end the thread
    while(!ctrl.done)
    {
        // start a new scope for the lock
        {
            std::unique_lock<std::mutex> lock{ctrl.mtx};

            // wait until do_hello become true
            ctrl.cv.wait(lock, [&]{ return ctrl.do_hello; });

            std::cout << " Hello";
            ctrl.do_hello = false; // signal hello has been done
        }
        // when the scope ends the lock is released

        ctrl.cv.notify_one(); // tell the waiting thread
   }
}

void world(hello_world_control& ctrl)
{
    while(!ctrl.done)
    {
        {
            std::unique_lock<std::mutex> lock{ctrl.mtx};

            // wait until do_hello become false
            ctrl.cv.wait(lock, [&]{ return !ctrl.do_hello; });

            std::cout << " World";
            ctrl.do_hello = true; // signal hello now needs to happen
        }

        ctrl.cv.notify_one(); // tell the waiting thread
    }
}

int main()
{
    hello_world_control ctrl;

    // use std::ref() to pass ctrl by reference to each thread
    std::thread t1{hello, std::ref(ctrl)};
    std::thread t2{world, std::ref(ctrl)};

    // let threads run for 100 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    // signal threads to stop looping
    ctrl.done = true;

    // synchronize with main thread
    t1.join();
    t2.join();
}

      



Output:

Hello World Hello World Hello World
Hello World Hello World Hello World
Hello World Hello World Hello World
...

      

+5


source







All Articles