深圳网站设计公司招聘,专业零基础网站建设教学服务,广东网站建设案例,免费网站大全推荐condition_variable介绍在C 11中#xff0c;我们可以使用条件变量#xff08;condition_variable#xff09;实现多个线程间的同步操作#xff1b;当条件不满足时#xff0c;相关线程被一直阻塞#xff0c;直到某种条件出现#xff0c;这些线程才会被唤醒。其主要成员函… condition_variable介绍在C 11中我们可以使用条件变量condition_variable实现多个线程间的同步操作当条件不满足时相关线程被一直阻塞直到某种条件出现这些线程才会被唤醒。其主要成员函数如下:条件变量是利用线程间共享的全局变量进行同步的一种机制主要包括两个动作一个线程因等待条件变量的条件成立而挂起另外一个线程使条件成立给出信号从而唤醒被等待的线程。为了防止竞争条件变量的使用总是和一个互斥锁结合在一起通常情况下这个锁是std::mutex并且管理这个锁 只能是 std::unique_lockstd::mutex RAII模板类。上面提到的两个步骤分别是使用以下两个方法实现等待条件成立使用的是condition_variable类成员wait 、wait_for 或 wait_until。给出信号使用的是condition_variable类成员notify_one或者notify_all函数。细节说明在条件变量中只能使用std::unique_lock std::mutex 说明unique_lock和lock_guard都是管理锁的辅助类工具都是RAII风格它们是在定义时获得锁在析构时释放锁。它们的主要区别在于unique_lock锁机制更加灵活可以再需要的时候进行lock或者unlock调用不非得是析构或者构造时。它们的区别可以通过成员函数就可以一目了然。在这里插入图片描述wait/wait_for说明线程的阻塞是通过成员函数wait()/wait_for()/wait_until()函数实现的。这里主要说明前面两个函数wait()成员函数函数声明如下void wait( std::unique_lockstd::mutex lock );
//Predicate 谓词函数可以普通函数或者lambda表达式
template class Predicate
void wait( std::unique_lockstd::mutex lock, Predicate pred );
wait 导致当前线程阻塞直至条件变量被通知或虚假唤醒发生可选地循环直至满足某谓词。wait_for()成员函数函数声明如下template class Rep, class Period
std::cv_status wait_for( std::unique_lockstd::mutex lock,const std::chrono::duration rel_time);template class Rep, class Period, class Predicate
bool wait_for( std::unique_lockstd::mutex lock,const std::chrono::duration rel_time,Predicate pred);
wait_for 导致当前线程阻塞直至条件变量被通知或虚假唤醒发生或者超时返回。返回值说明若经过 rel_time 所指定的关联时限则为 std::cv_status::timeout 否则为 std::cv_status::no_timeout 。若经过 rel_time 时限后谓词 pred 仍求值为 false 则为 false 否则为 true 。以上两个类型的wait函数都在会阻塞时自动释放锁权限即调用unique_lock的成员函数unlock以便其他线程能有机会获得锁。这就是条件变量只能和unique_lock一起使用的原因否则当前线程一直占有锁线程被阻塞。notify_all/notify_onenotify函数声明如下void notify_one() noexcept;
若任何线程在 *this 上等待则调用 notify_one 会解阻塞(唤醒)等待线程之一。void notify_all() noexcept;
若任何线程在 *this 上等待则解阻塞唤醒)全部等待线程。虚假唤醒在正常情况下wait类型函数返回时要不是因为被唤醒要不是因为超时才返回但是在实际中发现因此操作系统的原因wait类型在不满足条件时它也会返回这就导致了虚假唤醒。因此我们一般都是使用带有谓词参数的wait函数因为这种(xxx, Predicate pred )类型的函数等价于while (!pred()) //while循环解决了虚假唤醒的问题
{wait(lock);
}
原因说明如下假设系统不存在虚假唤醒的时代码形式如下if (不满足xxx条件)
{//没有虚假唤醒wait函数可以一直等待直到被唤醒或者超时没有问题。//但实际中却存在虚假唤醒导致假设不成立wait不会继续等待跳出if语句//提前执行其他代码流程异常wait();
}//其他代码
...
正确的使用方式使用while语句解决while (!(xxx条件) )
{//虚假唤醒发生由于while循环再次检查条件是否满足//否则继续等待解决虚假唤醒wait();
}
//其他代码
....
条件变量使用在这里我们使用条件变量解决生产者-消费者问题该问题主要描述如下生产者-消费者问题也称有限缓冲问题是一个多进程/线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个进程/线程——即所谓的“生产者”和“消费者”,在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中然后重复此过程。与此同时费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据消费者也不会在缓冲区中空时消耗数据。要解决该问题就必须让生产者在缓冲区满时休眠要么干脆就放弃数据等到下次消费者消耗缓冲区中的数据的时候生产者才能被唤醒开始往缓冲区添加数据。同样也可以让消费者在缓冲区空时进入休眠等到生产者往缓冲区添加数据之后再唤醒消费者。生产者-消费者代码如下std::mutex g_cvMutex;
std::condition_variable g_cv;//缓存区
std::dequeint g_data_deque;
//缓存区最大数目
const int MAX_NUM 30;
//数据
int g_next_index 0;//生产者消费者线程个数
const int PRODUCER_THREAD_NUM 3;
const int CONSUMER_THREAD_NUM 3;void producer_thread(int thread_id)
{while (true){std::this_thread::sleep_for(std::chrono::milliseconds(500));//加锁std::unique_lock std::mutex lk(g_cvMutex);//当队列未满时继续添加数据g_cv.wait(lk, [](){ return g_data_deque.size() MAX_NUM; });g_next_index ;g_data_deque.push_back(g_next_index);std::cout producer_thread: thread_id producer data: g_next_index;std::cout queue size: g_data_deque.size() std::endl;//唤醒其他线程 g_cv.notify_all();//自动释放锁}
}void consumer_thread(int thread_id)
{while (true){std::this_thread::sleep_for(std::chrono::milliseconds(550));//加锁std::unique_lock std::mutex lk(g_cvMutex);//检测条件是否达成g_cv.wait( lk, []{ return !g_data_deque.empty(); });//互斥操作消息数据int data g_data_deque.front();g_data_deque.pop_front();std::cout \tconsumer_thread: thread_id consumer data: ;std::cout data deque size: g_data_deque.size() std::endl;//唤醒其他线程g_cv.notify_all();//自动释放锁}
}int main()