2024年最全C++11条件变量使用详解_c++11条件变量 使用,2024年最新干货面试教程

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

                     const std::chrono::duration<Rep, Period>& rel_time);

(2)
template< class Rep, class Period, class Predicate >
bool wait_for( std::unique_lockstd::mutex& lock,
const std::chrono::duration<Rep, Period>& rel_time,
Predicate pred);

 wait\_for 导致当前线程阻塞直至条件变量被**通知**,或**虚假唤醒发生**,或者**超时返回**。

 返回值说明:

 (1)若经过 rel\_time 所指定的关联时限则为 std::cv\_status::timeout ,否则为 std::cv\_status::no\_timeout 。

 (2)若经过 rel\_time 时限后谓词 pred 仍求值为 false 则为 false ,否则为 true 。


以上两个类型的**wait函数都在会阻塞时,自动释放锁权限,即调用unique\_lock的成员函数unlock(),以便其他线程能有机会获得锁**。这就是条件变量只能和unique\_lock一起使用的原因,否则当前线程一直占有锁,线程被阻塞。


**notify\_all/notify\_one**


notify函数声明如下:



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::deque 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();
//自动释放锁
}
}

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

术提升。**

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值