多线程-条件变量

条件变量 std::condition_variable,类似Linux C中的pthread_cond_t

可以用来阻塞当前线程,等待其他线程通知解除阻塞

condition_variable

std::condition_variable  条件变量类
void condition_variable::wait(unique_lock<mutex>&) / (unique_lock<mutex>& lck, Predicate pred)
//阻塞当前线程,两个参数的重载函数:设置了条件,有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞
//pred是一个bool返回值的函数

bool condition_variable::wait_for(unique_lock<mutex>&, chrono::duration)
bool condition_variable::wait_until(unique_lock<mutex>&, chrono::time_point)
//阻塞当前线程,加入超时控制,超过设置的时间,线程也结束阻塞
//wait_for是指定时间长度,wait_until是指定时间点
//这两个函数都有带设置条件的重载,当超时时:即使条件为false也会结束阻塞,当未超时:和wait函数的判断逻辑一致
//如果是超时退出的,则返回false;接收到通知或满足条件退出的,返回true

condition_variable::notify_all()
//通知所有线程解除阻塞
condition_variable::notify_one()
//通知一个线程解除阻塞,具体是哪个没法控制

std::condition_variable示例:

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

std::mutex lmutex;
std::condition_variable cond;
bool flag1=false;

bool seq_func() {
    return flag1;
}

void func1() {
    std::unique_lock<std::mutex> lck(lmutex);

    std::cout << "func1 before wait" << std::endl;
    //线程阻塞,但这时不会占用lck,等结束阻塞后lck状态会恢复之前的状态
    cond.wait(lck);  
    std::cout << "func1 after wait" << std::endl;
}

void func2() {
    std::unique_lock<std::mutex> lck(lmutex);

    std::cout << "func2 before wait" << std::endl;
    //判断seq_func返回值,若为false,则阻塞线程;否则不阻塞
    //线程阻塞时不会占用lck,等结束阻塞后lck状态会恢复之前的状态
    //接收到通知后,判断seq_func的返回值,若为true,结束阻塞;否则继续阻塞
    cond.wait(lck, seq_func);  
    std::cout << "func2 after wait" << std::endl;
}

void func3() {
    std::unique_lock<std::mutex> lck(lmutex);

    std::cout << "func3 before wait" << std::endl;
    //设置超时时间1s,超时则结束阻塞
    //如果在时间内收到解除阻塞通知,则要判断seq_func的返回值,为true退出,为false继续阻塞
    bool stat = cond.wait_for(lck, std::chrono::seconds(1), seq_func);
    std::cout << "func3 after wait, stat: " << stat <<  std::endl;
}

int main() {
    std::thread th1(func1);
    std::thread th2(func2);
    std::thread th3(func3);

    std::this_thread::sleep_for( std::chrono::seconds(10) );

    //验证:经过延迟10s,线程1处于阻塞状态,但并未占用lmutex,主线程还可以获取锁
    std::unique_lock<std::mutex> lck(lmutex);
    std::cout << "main thread func " << std::endl;
    std::this_thread::sleep_for( std::chrono::milliseconds(200) );
    lck.unlock();

    flag1 = true;  //将条件置true
    std::cout << "notify all" << std::endl;
    cond.notify_all();  //通知所有线程,解除阻塞

    th1.join();
    th2.join();
    th3.join();
    return 0;
}

参考:

pthread_cond_xxx

《APUE》11章-线程

pthread_cond_xxx示例

/***
 * 条件变量,配合互斥量使用。对互斥量进行条件保护
 * pthread_cond_t  条件变量类型 
 * pthread_cond_init  初始化
 * pthread_cond_wait/pthread_cond_timedwait  等待/超时等待条件变量的信号,等待时释放指定的互斥量,等待结束重新锁住互斥量
 * pthread_cond_signal/pthread_cond_broadcast  向 至少一个/全部 等待该条件变量的线程发送信号
 * pthread_cond_destroy  销毁
 * *******/

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

typedef struct{
    int age;
    int count;
}PersonType;

pthread_cond_t qCond;
pthread_mutex_t qMutex;

void *pth_handler(void *arg){
    //pthread_mutex_lock( &qMutex);
    printf("cond wait start\n");
    //把锁住的互斥量传入函数
    pthread_cond_wait( &qCond, &qMutex);  //等待条件变量信号,这时候不占用互斥量
    //等wait函数返回时,互斥量再次被锁住
    printf("cond wait done\n");

    PersonType * p = arg;
    p->count++;
    p->age = 20;

    //pthread_mutex_unlock( &qMutex);

    pthread_exit(NULL);
}

int main(){
    pthread_t pthid;  //线程ID
    PersonType p1 = {0,0};

    pthread_mutex_init( &qMutex, NULL);
    pthread_cond_init( &qCond, NULL);  //初始化条件变量

    if( 0 != pthread_create(&pthid, NULL, pth_handler, (void *)&p1)){
        printf("pthread create failed\n");
    }

    sleep(1);  //让刚才创建的线程先执行

    //修改p1
    printf("main sleep done\n");
    pthread_mutex_lock( &qMutex); 

    p1.age = 10;
    p1.count++;
    printf("main change p\n");

    pthread_mutex_unlock( &qMutex);

    printf("cond signal\n");
    //pthread_cond_signal( &qCond);  //向至少一个等待该条件变量的线程发信号
    pthread_cond_broadcast( &qCond);  //向所有等待该条件变量的线程发信号


    pthread_cond_destroy(&qCond);
    pthread_mutex_destroy(&qMutex);

    pthread_join( pthid, NULL);

    printf("age = %d, count = %d\n", p1.age, p1.count);
}
posted @ 2023-03-02 22:02  WuYunTaXue  阅读(30)  评论(0编辑  收藏  举报