条件变量中的while和if

文章讨论了在线程池实现中,为何在提交任务的接口中使用while而非if进行条件判断。由于可能存在多个生产者和消费者线程改变条件状态,使用while能确保在每次获取锁后都重新检查条件,防止任务队列超出最大容量。if可能导致已满足条件的线程在被唤醒后不再检查,从而产生错误。线程在被唤醒后会重新竞争锁,并从wait()之后的代码继续执行。
摘要由CSDN通过智能技术生成

最近复习线程池源码时,产生以下疑问,记录一下。

条件变量判断时,何时用while,何时用if?

个人总结一下:

条件可能被多个线程改变,使用while;条件只被一个线程改变,使用if。

具体细节:

该代码片段为线程池中,功能为给用户提供的任务提交接口,即生产者线程生产任务。

void ThreadPool::submitTask(std::shared_ptr<Task> sp) {   // 提交任务
    std::unique_lock<std::mutex> lock(taskQueMtx_); // 获取锁
    
    while (taskQue_.size() == taskQueMaxThreshold_) { // 任务队列满了的话
        notFull_.wait(lock); // 则释放锁,等待不满。
    } 

    // notFull_.wait(lock, [&]()->bool {  // 等待任务队列不满
    //   return taskQue_.size() < taskQueMaxThreshold_; 
          // 任务队列任务数 < 阈值,则return true,条件成立,可继续往下执行。
    //  });   // wait()函数该重载形式,等价于上面while判断 + wait().
    
    taskQue_.emplace(sp);   // 不满则提交任务。
    taskSize_++;
    notEmpty_.notify_all();  // 通知消费者线程任务队列不空
}

第四行 为什么使用while,而不是if ?

线程池为生产者-消费者模型,存在多个生产者、消费者线程。多个生产者、消费者线程都有可能改变条件状态。

若使用if,会出现如下情况:

  1. 生产者线程A第一次进行if判断,发现taskQue_.size() == taskQueMaxThreshold_条件成立,则释放锁、阻塞在notFull_上;

  1. 后续消费者消费了n个(n>0)任务,使得taskQue_.size() < taskQueMaxThreshold_, 同时唤醒了被阻塞生产者线程A,线程A被唤醒后,仍需要获取锁后,才能继续往下执行;

  1. 若锁被生产者线B程抢走,线程B生产了n个(n>0)任务,此时taskQue_.size() == taskQueThreshold_条件再次成立,生产者线程B释放了锁;

  1. 此时生产者线程A抢到锁,线程A继续从上次被阻塞的地方(if判断)开始执行,因为是if判断,线程A上次已经判断过了,则不再判断,继续往下执行,导致taskQue_.size() = taskQueMaxThreshold时,线程A继续生产任务,使得任务队列中任务数超过最大任务数,出错。

若为while判断,则线程A被唤醒且抢到锁后,仍需while循环进行条件判断。此时while循环发现任务队列再次被其他生产者线程填满,则继续等待。
上述代码注意事项:

线程被唤醒后,会重新竞争锁。

得到锁后,则从wait()函数下一行继续执行,即继续进行while判断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的马师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值