条件变量与线程同步

发布时间:2026/5/26 17:43:29

条件变量与线程同步 条件变量的作用就是阻塞/唤醒线程空轮询与阻塞空轮询也能实现阻塞条件变量使线程阻塞二者的区别是什么空轮询浪费CPU资源因为他要不断抢夺CPU时间片而条件变量是将线程从就绪态转换为阻塞态。二者有着本质的不同。条件变量与线程同步的关系如果说是两个线程12间的同步那么条件变量和互斥锁是一样的二者都是通过阻塞线程2实现线程1对共享资源的独占当线程1执行完后互斥锁释放锁线程2加锁访问共享资源条件变量唤醒线程2阻塞线程1如果多个线程间的同步条件变量的阻塞与唤醒往往是一批所以当一批线程被唤醒时势必会同时访问共享资源此时要加锁控制既然“锁”能实现线程同步为什么要使用“条件变量锁”场景多线程环境中符合某些条件后才能访问共享资源比如生产者-消费者模型如果没有条件变量那么方案是空轮询锁 资源开销大如果有条件变量那么方案是条件变量锁 节省资源条件变量这里使用C11的新特性C11中有两种条件变量分别是condition_variablecondition_variable_any他们是两个类通过类的实例化对象作为条件变量在一个线程方法中执行条件变量的阻塞方法那么条件变量内部将会记录被阻塞线程的ID在“多线程环境中符合某些条件后才能访问共享资源”场景中需要配合锁来使用condition_variable配合std::unique_lockstd::mutexcondition_variable_any可配合任意的带有lock() unlock()的四种mutex使用阻塞方法wait()与wait_for()唤醒方法notify_one()与notify_all()既然条件变量锁方案非常适合“多线程环境中符合某些条件后才能访问共享资源”场景中那么对于“条件”的书写就是wait或wait_for方法的最后一个参数可以传入一个回调函数或者利用lambda写一个匿名函数通过这个函数的bool返回值来判断是否符合“条件”。生产者-消费者模型生产者消费者模型的核心是任务队列简单的话一个队列就实现了复杂的话classSTL容器因为STL容器适合作为任务队列的数据结构一方面有方便快捷的api另一方面大小是动态变化的。主要成员方法取对象放对象判断是否为空判断是否为满容器里面有多少任务除此之外一般还会定义任务队列的最大容量。注意线程在从就绪态切换到阻塞态会释放锁资源线程也只有在就绪态才有抢锁资格详见下面的代码中put与take方法。下面是使用condition_variable来实现的生产者-消费者模型// 任务队列类 class TaskQueue { public: // 添加数据--生产者线程的线程函数 void put(const int task) { lock_guardmutex locker(myMutex); //当多个生产者线程(就绪态)同时执行到这一步线程1抢到锁资源其它线程(就绪态)阻塞在这里 while (taskQueue.size() maxSize) //此时队列已满 { //线程1被切换到阻塞态释放锁。 //此时阻塞在上上行的其它就绪态线程就会抢夺锁资源抢到锁资源的线程也被切换到阻塞态 //随后依次所有的就绪态线程切换到阻塞态阻塞到这里 //当所有线程被notify后重新抢夺锁资源执行共享资源区 notFull.wait(myMutex); } //共享资源区锁1唤醒后枷锁锁2函数第一行加锁 taskQueue.push(task); cout 添加任务: task , 线程ID: this_thread::get_id() endl; // 唤醒消费者 notEmpty.notify_all(); } // 取数据0--消费者线程的线程函数 void take() { unique_lockmutex locker(myMutex);//当多个消费者线程(就绪态)同时执行到这一步线程1抢到锁资源其它线程(就绪态)阻塞在这里 while(taskQueue.empty())//此时队列已空 { //线程1被切换到阻塞态释放锁。 //此时阻塞在上上行的其它就绪态线程就会抢夺锁资源抢到锁资源的线程也被切换到阻塞态 //随后依次所有的就绪态线程切换到阻塞态阻塞到这里 //当所有线程被notify后重新抢夺锁资源执行共享资源区 notEmpty.wait(locker); } //共享资源区锁1唤醒后枷锁锁2函数第一行加锁 int node taskQueue.front(); taskQueue.pop(); cout 删除任务: node , 线程ID: this_thread::get_id() endl; // 唤醒生产者 notFull.notify_all(); } bool isFull() { lock_guardmutex locker(myMutex); if (maxSize taskQueue.size()) { return true; } return false; } bool isEmpty() { lock_guardmutex locker(myMutex); if (taskQueue.size() 0) { return true; } return false; } int taskSize() { lock_guardmutex locker(myMutex); return taskQueue.size(); } private: int maxSize 100; queueint taskQueue; mutex myMutex; condition_variable_any notFull; // 阻塞(唤醒)生产者线程的条件变量 condition_variable_any notEmpty; // 阻塞(唤醒)消费者线程的条件变量 }; int main() { thread t1[5]; //生产者线程数组 thread t2[5]; //消费者线程数组 TaskQueue taskQ; for (int i 0; i 5; i) { t1[i] thread(TaskQueue::put, taskQ, 100i); //线程函数是TaskQueue::put参数100i(任务ID是100i) t2[i] thread(TaskQueue::take, taskQ); } //要保证 所有子线程不退出 for (int i 0; i 5; i) { t1[i].join(); t2[i].join(); } return 0; }生产者线程函数put多个生产者通过锁来竞争临界共享资源一个一个来此时任务队列满了通过条件变量将这些生产者线程都阻塞到这里wait等待被唤醒notFull.wait消费者线程函数take多个消费者通过锁来竞争临界共享资源一个一个来此时任务队列空了通过条件变量将这些消费者线程都阻塞到这里wait等待被唤醒可以唤醒生产者notFull.notify_all()参考C线程同步之条件变量 | 爱编程的大丙大炳课堂C11新特性-条件变量

相关新闻