windows编程线程同步之条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待 “条件变量的条件成立” 而挂起;另一个线程使 “条件成立”,然后唤醒另一个等待线程。
C++11提供了多线程线程同步的条件变量接口可以调用,一般都需要配合互斥量进行使用,常说到的生产者-消费者模型就可以用条件变量进行实现。
主要用到的结果接口有:

void wait(std::unique_lock<std::mutex>& lock);

//Predicate 谓词函数,第二个参数可为lambda表达式或函数
template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

template< class Rep, class Period >
std::cv_status wait_for( std::unique_lock<std::mutex>& lock,
                         const std::chrono::duration<Rep, Period>& rel_time);

template< class Rep, class Period, class Predicate >
bool wait_for( std::unique_lock<std::mutex>& lock,
               const std::chrono::duration<Rep, Period>& rel_time,
               Predicate pred);
               
void notify_one() noexcept;
唤醒等待线程其中一个。

void notify_all() noexcept;
唤醒全部等待线程。


接下来给一个例子,有3个线程,1个线程“生产消息”,另外两个线程“等待唤醒”,当消息队列中有消息的时候,唤醒另外两个线程去拿消息。当消息队列中无消息时,另外两个线程挂起,“生产”线程开始生产消息。

#include 
#include 
#include 
#include 
#include 
using namespace std;
class A{
     
public:
    void inMsgRecvQueue(){
     
        for(int i = 0; i < 100000; ++i){
     
            printf("插入消息队列: %d, 线程id: %d\n", i, this_thread::get_id());
            unique_lock<mutex> sbguard1(my_mutexl);
            msgRecvQueue.push_back(i);  //将消息加入消息队列
            my_cond.notify_one();  //唤醒被wait的那个线程
            /*
              但此时被唤醒的线程不一定就处在wait, 他可能已经逃离wait并且
              获取到消息, 在做一些处理了(且已经放锁, 不然本线程不可能拿到锁走到这)
              那此时的notify_one就无效
            */
        }
        return;
    }
    void outMsgRecvQueue(){
     
        while(true){
     
            unique_lock<mutex> sbguardl(my_mutexl);
            //wait()第二个参数返回值为false, wait将解锁, 然后阻塞在wait这一行
            //直到其他某个线程调用notify_one()成员函数将该线程唤醒.
            //如果没有第二个参数, 该线程也会阻塞在wait这一行, 等待唤醒 
            my_cond.wait(sbguardl, [this](){
     
                if(!msgRecvQueue.empty()){
     
                    return true; //有信息就往下取, 否则就阻塞
                }
                return false;
            });

            //此时处于锁状态,消息队列中至少有1条数据
            printf("从消息队列中取出: %d, 线程id: %d\n", msgRecvQueue.front(), this_thread::get_id());
            msgRecvQueue.pop_front();
            sbguardl.unlock();  //拿完数据,记得解锁

            //下面可以对数据做一些处理......
        }
    }
    void outMsgRecvQueue1(){
     
        while(true){
     
            unique_lock<mutex> sbguardl(my_mutexl);
            //wait()第二个参数返回值为false, wait将解锁, 然后阻塞在wait这一行
            //直到其他某个线程调用notify_one()成员函数将该线程唤醒.
            //如果没有第二个参数, 该线程也会阻塞在wait这一行, 等待唤醒 
            my_cond.wait(sbguardl, [this](){
     
                if(!msgRecvQueue.empty()){
     
                    return true; //有信息就往下取, 否则就阻塞
                }
                return false;
            });

            //此时处于锁状态,消息队列中至少有1条数据
            printf("从消息队列中取出: %d, 线程id: %d\n", msgRecvQueue.front(), this_thread::get_id());
            msgRecvQueue.pop_front();
            sbguardl.unlock();  //拿完数据,记得解锁

            //下面可以对数据做一些处理......
        }
    }
private:
    list<int> msgRecvQueue;
    mutex my_mutexl;
    condition_variable my_cond;
};
int main(int argc, char const *argv[])
{
     
    /*
      condition_variable / wait() / notify_one()
      线程A: 等待一个条件满足就唤醒B
      线程B: 做一些业务
      条件变量配合互斥量一起使用, 生产者-消费者
      两个线程共用一把锁, 竞争取锁
      wait会不断尝试拿锁, 拿到锁后就往下执行
      如果wait有第二个参数,则由第二个参数返回值决定
      false - 解锁,休眠,等待被唤醒   true - wait返回,继续往下走(可以通过lambda表达式判定)
    */
    A myobj;
    thread b(&A::inMsgRecvQueue, &myobj);
    thread a(&A::outMsgRecvQueue, &myobj);
    thread c(&A::outMsgRecvQueue1, &myobj);
    a.join();
    b.join();
    c.join();
    return 0;
}

结果(线程id = 2的为“生产者”,线程id = 3和线程id = 4的为“消费者”):
windows编程线程同步之条件变量_第1张图片

你可能感兴趣的:(多线程,线程同步,c++,多线程)