如何避免工作线程因错过condition_variable通知而闲置,导致任务积压
解决条件变量通知丢失导致任务积压的问题
问题描述
我有若干工作线程等待队列中的任务。通常生产者调用
produce函数后,会调用notify_one让工作线程解除阻塞并执行。但如果生产者生产任务时,没有线程在condition_variable上等待,此时notify_one不会产生任何作用。之后工作线程(消费者)调用wait时,会错过之前的通知,导致队列中已有任务,却要等到生产者再次调用notify才会处理,届时队列会积压两个任务。请问如何避免队列有任务等待,同时存在可用工作线程的情况?
原示例代码
struct ThreadPool { static inline std::mutex mutex; static inline std::condition_variable condition_var; static inline int number_of_things_to_consume = 0; static void consume() { std::unique_lock lock{ mutex }; /* 这里可能出现队列有任务但工作线程还在等待的情况 - 因为生产者调用了notify_one但当时没有线程在等待 */ condition_var.wait(lock, []() {return number_of_things_to_consume > 0; }); /* 消费逻辑 */ } static void produce() { std::unique_lock lock{ mutex }; ++number_of_things_to_consume; condition_var.notify_one(); } };
解决方案
你其实不用太担心这个问题——因为代码里用的是带谓词的condition_variable::wait,它的工作机制已经从根源上避免了这种情况。
带谓词的wait等价于以下逻辑:
while (!pred()) { wait(lock); }
也就是说,当消费者线程执行到wait时,会先检查谓词条件(这里是number_of_things_to_consume > 0):
- 如果条件已经满足(比如生产者已经生产了任务),线程不会进入阻塞,直接跳过
wait执行后续的消费逻辑; - 只有条件不满足时,才会释放锁并进入等待状态,等待生产者的通知。
所以哪怕生产者先调用了notify_one但当时没有线程等待,后续消费者线程到达wait时,会直接发现任务已经存在,不会傻等下一次通知。
如果要让逻辑更严谨、贴合实际场景,还可以做以下优化:
- 用
std::queue代替单纯的计数变量,存储实际任务,避免计数和任务数量不一致的问题; - 消费任务时,拿到任务后及时解锁,不要让锁长时间持有,提升并发效率。
优化后的代码示例
#include <mutex> #include <condition_variable> #include <queue> struct ThreadPool { static inline std::mutex mutex; static inline std::condition_variable condition_var; static inline std::queue<int> task_queue; // 用队列存储具体任务 static void consume() { std::unique_lock lock{ mutex }; // 先检查队列是否非空,非空直接执行消费,不进入等待 condition_var.wait(lock, []() {return !task_queue.empty(); }); // 取出任务 int task = task_queue.front(); task_queue.pop(); lock.unlock(); // 提前解锁,避免处理任务时占用锁 // 这里写任务处理逻辑... } static void produce(int task) { std::unique_lock lock{ mutex }; task_queue.push(task); condition_var.notify_one(); } };
内容的提问来源于stack exchange,提问作者Zebrafish
相关产品推荐
相关产品推荐

