From the point of view more than one thread title condition variable in C ++ 11 std :: condition_variable of use and principles

Now there is a pen questions like this below.

There are two threads, one thread loop output A, another thread loop output B, how to make these two threads stable output in the console ABABAB….

Without thinking too much, we will certainly define a variable signs isTurnA, isTurnAas trueoutput A, output the same token B, it is one of the most simple finite state machine, just follow the state machine, then certainly agree the answer. isTurnAIs shared data, so atomic variables or mutex to protect, the mutex used here std::mutex, the code follows:

class TypeAB
{
public:
    TypeAB() : _isTurnA(true) {}
    ~TypeAB()
    {
        _threadA.join();
        _threadB.join();
    }
    void PrintAB()
    {
        _threadA = std::thread(&TypeAB::TypeA, this);
        std::this_thread::sleep_for(std::chrono::seconds(2));
        _threadB = std::thread(&TypeAB::TypeB, this);
    }
private:
    std::mutex _mutex;
    bool _isTurnA;

    std::thread _threadA;
    std::thread _threadB;

    void TypeB()
    {
        for (int cnt = 0; cnt < 10; std::this_thread::sleep_for(std::chrono::seconds(3)))
        {
            std::unique_lock<std::mutex> lock(_mutex);
            for (; _isTurnA; )
            {
                lock.unlock();
                lock.lock();
            }
            std::cout << std::this_thread::get_id() << " - B : " << cnt++ << std::endl;
            _isTurnA = true;
        }
    }

    void TypeA()
    {
        for (int cnt = 0; cnt < 10; std::this_thread::sleep_for(std::chrono::seconds(1)))
        {
            std::unique_lock<std::mutex> lock(_mutex);
            for (; !_isTurnA; )
            {
                lock.unlock();
                lock.lock();
            }
            std::cout << std::this_thread::get_id() << " - A : " << cnt++ << std::endl;
            _isTurnA = false;
        }
    }
};

This code is running, but look at the following cycle:

for (; !_isTurnA; )
{
    lock.unlock();
    lock.lock();
}

Continue to unlock the lock, so that another thread has a chance to get a lock, like this do-consuming efficiency. Then we come to a blocking operation:

for (; !_isTurnA; )
{
    lock.unlock();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    lock.lock();
}

Like this will bring about a problem, it may just blocking completed and access to the lock, this time, just set up another thread isTurnA, this situation will cause the current thread blocked more than once. Therefore, blocking this difficult time balancing the blocking time is too short, then spin and there is no difference; blocking time is too long, it will lead to threading issues not timely.

This was back events Event (on the condition variable package) C ++ in that example in the train, condition variables that way, it works as follows:

for (; !_isTurnA; )
{
    lock.unlock();
    // 线程阻塞于此,等待一个信号,有这个信号就不阻塞了
    lock.lock();
}

Specific can see in C ++ event Event (on the condition variable package) introduced on condition variable. Here are standard answer to that question is multi-threaded Road:

#include <bits/stdc++.h>

class TypeAB
{
public:
    ~TypeAB()
    {
        _threadA.join();
        _threadB.join();
    }
    void PrintAB()
    {
        _threadA = std::thread(&TypeAB::TypeA, this);
        std::this_thread::sleep_for(std::chrono::seconds(2));
        _threadB = std::thread(&TypeAB::TypeB, this);
    }
private:
    std::mutex _mutex;
    std::condition_variable _condi;
    bool _isTurnA = true;

    std::thread _threadA;
    std::thread _threadB;

    void TypeB()
    {
        for (int cnt = 0; cnt < 10; std::this_thread::sleep_for(std::chrono::seconds(3)))
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _condi.wait(lock, [this] () -> bool { return !_isTurnA; });
            std::cout << std::this_thread::get_id() << " - B : " << cnt++ << std::endl;
            _isTurnA = true;
            lock.unlock();

            _condi.notify_one();
        }
    }

    void TypeA()
    {
        for (int cnt = 0; cnt < 10; std::this_thread::sleep_for(std::chrono::seconds(1)))
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _condi.wait(lock, [this] () -> bool { return _isTurnA; });
            std::cout << std::this_thread::get_id() << " - A : " << cnt++ << std::endl;
            _isTurnA = false;
            lock.unlock();

            _condi.notify_one();
        }
    }
};

int main()
{
    TypeAB().PrintAB();
    return 0;
}

Now take a look at std::condition_variablethe source code section:

// VC14的mutex头文件
class condition_variable
{ // class for waiting for conditions
  public:
    typedef _Cnd_t native_handle_type;

    condition_variable()
    { // construct
        _Cnd_init_in_situ(_Mycnd());
    }

    ~condition_variable() _NOEXCEPT
    { // destroy
        _Cnd_destroy_in_situ(_Mycnd());
    }

    condition_variable(const condition_variable &) = delete;
    condition_variable &operator=(const condition_variable &) = delete;

    void notify_one() _NOEXCEPT
    { // wake up one waiter
        _Cnd_signalX(_Mycnd());
    }

    void notify_all() _NOEXCEPT
    { // wake up all waiters
        _Cnd_broadcastX(_Mycnd());
    }

    void wait(unique_lock<mutex> &_Lck)
    { // wait for signal
        _Cnd_waitX(_Mycnd(), _Lck.mutex()->_Mymtx());
    }

    template <class _Predicate>
    void wait(unique_lock<mutex> &_Lck, _Predicate _Pred)
    { // wait for signal and test predicate
        while (!_Pred())
            wait(_Lck);
    }
}

The following code verification questions:

  • waitThe wording of the second recommendation, can prevent false wake
  • Condition variables need to match the lock to use, and can only be std::unique_locktwo reasons, std::unique_lockthere are lock unlock member function works exactly fit condition variable; RAII mode, you can guarantee exception safety
  • Condition variables can not copy configuration and assignment configuration (two constructors are removed), but may be moved ah (rvalue constructor is not deleted), which also std::condition_variableconstitutes std::futureforeshadowed
Published 299 original articles · won praise 353 · Views 450,000 +

Guess you like

Origin blog.csdn.net/FlushHip/article/details/88311749