Home > Net >  Correct way to check bool flag in thread
Correct way to check bool flag in thread

Time:07-11

How can I check bool variable in class considering thread safe?

For example in my code,

// test.h

class Test {
    void threadFunc_run();
    void change(bool _set) { m_flag = _set; }

    ...
    bool m_flag;
};


// test.cpp

void Test::threadFunc_run()
{
    // called "Playing"
    while(m_flag == true) {
        for(int i = 0; i < 99999999 && m_flag; i  ) {
            // do something .. 1
        }

        for(int i = 0; i < 111111111 && m_flag; i  ) {
            // do something .. 2
        }
    }
}

I wan to stop "Playing" as soon as change(..) function is executed in the external code.

It also wants to be valid in process of operating the for statement.

According to the search, there are variables for recognizing immediate changes, such as atomic or volatile.

If not immediately, is there a better way to use a normal bool ?

CodePudding user response:

Actually synchronizing threads safely requires more then a bool. You will need a state, a mutex and a condition variable like this. The approach also allows for quick reaction to stop from within the loop.

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <future>
#include <mutex>

class Test
{
private:
    // having just a bool to check the state of your thread is NOT enough. 
    // your thread will have some intermediate states as well
    enum play_state_t
    {
        idle,           // initial state, not started yet (not scheduled by OS threadscheduler yet)
        playing,        // running and doing work
        stopping,       // request for stop is issued
        stopped         // thread has stopped (could also be checked by std::future synchronization).
    };

public:
    void play()
    {
        // start the play loop, the lambda is not guaranteed to have started
        // after the call returns (depends on threadscheduling of the underlying OS)
        // I use std::async since that has far superior synchronization with the calling thead
        // the returned future can be used to pass both values & exceptions back to it.
        m_play_future = std::async(std::launch::async, [this]
        {
            // give a signal the asynchronous function has really started
            set_state(play_state_t::playing);
            std::cout << "play started\n";

            // as long as state is playing keep doing the work
            while (get_state() == play_state_t::playing)
            {
                // loop to show we can break fast out of it when stop is called
                for (std::size_t i = 0; (i < 100l) && (get_state() == play_state_t::playing);   i)
                {
                    std::cout << ".";
                    std::this_thread::sleep_for(std::chrono::milliseconds(200));
                }
            }

            set_state(play_state_t::stopped);
            std::cout << "play stopped.\n";
        });

        // avoid race conditions really wait for
        // trhead handling async to have started playing
        wait_for_state(play_state_t::playing);
    }

    void stop()
    {
        std::unique_lock<std::mutex> lock{ m_mtx }; // only wait on condition variable in lock
        if (m_state == play_state_t::playing)
        {
            std::cout << "\nrequest stop.\n";
            m_state = play_state_t::stopping;
            m_cv.wait(lock, [&] { return m_state == play_state_t::stopped; });
        }
    };

    ~Test()
    {
        stop();
    }

private:

    void set_state(const play_state_t state)
    {
        std::unique_lock<std::mutex> lock{ m_mtx }; // only wait on condition variable in lock
        m_state = state;
        m_cv.notify_all();              // let other threads that are wating on condition variable wakeup to check new state
    }

    play_state_t get_state() const
    {
        std::unique_lock<std::mutex> lock{ m_mtx }; // only wait on condition variable in lock
        return m_state;
    }

    void wait_for_state(const play_state_t state)
    {
        std::unique_lock<std::mutex> lock{ m_mtx };
        m_cv.wait(lock, [&] { return m_state == state; });
    }

    // for more info on condition variables 
    // see : https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables

    mutable std::mutex m_mtx;
    std::condition_variable m_cv;       // a condition variable is not really a variable more a signal to threads to wakeup
    play_state_t m_state{ play_state_t::idle };
    std::future<void> m_play_future;
};


int main()
{
    Test test;
    test.play();
    std::this_thread::sleep_for(std::chrono::seconds(1));
    test.stop();

    return 0;
}
  • Related