C++并发编程实战:条件变量深度解析与应用指南

C++并发编程实战:条件变量深度解析与应用指南

Cplusplus-Concurrency-In-Practice A Detailed Cplusplus Concurrency Tutorial 《C++ 并发编程指南》 Cplusplus-Concurrency-In-Practice 项目地址: https://gitcode.com/gh_mirrors/cp/Cplusplus-Concurrency-In-Practice

引言

在多线程编程中,条件变量(Condition Variable)是一种重要的线程同步机制,它允许线程在某些条件不满足时主动阻塞,并在条件满足时被唤醒。本文将深入探讨C++标准库中的条件变量实现,帮助开发者掌握这一强大的并发编程工具。

条件变量基础概念

条件变量总是与互斥量(mutex)配合使用,主要解决线程间的"等待-通知"问题。其核心思想是:当某个条件不满足时,线程可以释放锁并进入等待状态;当其他线程改变了条件并发出通知时,等待的线程被唤醒并重新获取锁。

std::condition_variable详解

构造函数

std::condition_variable只提供默认构造函数,拷贝构造函数被显式删除:

std::condition_variable cv1;       // 正确
std::condition_variable cv2 = cv1; // 错误,拷贝构造被禁用

等待机制

基本wait操作
void wait(std::unique_lock<std::mutex>& lck);

当线程调用wait时,它会:

  1. 自动释放锁(lck.unlock())
  2. 进入阻塞状态等待通知
  3. 收到通知后重新获取锁(lck.lock())
带谓词的wait
template <class Predicate>
void wait(std::unique_lock<std::mutex>& lck, Predicate pred);

这种形式相当于:

while(!pred()) {
    wait(lck);
}

只有当pred返回false时才阻塞,收到通知后只有当pred返回true才会继续执行。

定时等待

wait_for
template <class Rep, class Period>
cv_status wait_for(std::unique_lock<std::mutex>& lck, 
                  const std::chrono::duration<Rep,Period>& rel_time);

在指定时间段内等待通知,超时返回cv_status::timeout,否则返回cv_status::no_timeout

wait_until
template <class Clock, class Duration>
cv_status wait_until(std::unique_lock<std::mutex>& lck,
                    const std::chrono::time_point<Clock,Duration>& abs_time);

在指定时间点前等待通知,行为与wait_for类似。

通知机制

notify_one

唤醒一个等待线程。如果有多个线程在等待,选择哪个线程是不确定的。

notify_all

唤醒所有等待线程。

条件变量使用模式

生产者-消费者模式

std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;

void producer() {
    while(true) {
        std::unique_lock<std::mutex> lock(mtx);
        data_queue.push(42);
        cv.notify_one();
    }
}

void consumer() {
    while(true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{return !data_queue.empty();});
        int data = data_queue.front();
        data_queue.pop();
        // 处理数据
    }
}

线程池任务分发

std::vector<std::thread> workers;
std::mutex queue_mutex;
std::condition_variable condition;
std::queue<std::function<void()>> tasks;

void worker_thread() {
    while(true) {
        std::function<void()> task;
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            condition.wait(lock, []{return !tasks.empty();});
            task = tasks.front();
            tasks.pop();
        }
        task();
    }
}

std::condition_variable_any

std::condition_variable_any是更通用的条件变量实现,可以与任何满足Lockable概念的类型配合使用,而不仅限于std::unique_lock<std::mutex>。但在性能上通常略逊于std::condition_variable

常见陷阱与最佳实践

  1. 虚假唤醒:即使没有通知,等待的线程也可能被唤醒。因此总是使用带谓词的wait形式。

  2. 丢失唤醒:在调用wait之前发出通知会导致通知丢失。确保状态改变和通知是原子操作。

  3. 锁的作用域:通知时不需要持有锁,实际上不持有锁时通知效率更高。

  4. 性能考虑:频繁的信号可能引起"惊群效应",考虑是否需要notify_all或notify_one。

总结

条件变量是C++多线程编程中强大的同步原语,正确使用可以高效解决复杂的线程同步问题。理解其工作原理和使用模式,能够帮助开发者编写出更健壮、高效的并发程序。记住核心原则:总是与互斥量配合使用,正确处理虚假唤醒,并合理选择通知策略。

Cplusplus-Concurrency-In-Practice A Detailed Cplusplus Concurrency Tutorial 《C++ 并发编程指南》 Cplusplus-Concurrency-In-Practice 项目地址: https://gitcode.com/gh_mirrors/cp/Cplusplus-Concurrency-In-Practice

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

申华昶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值