线程间同步,条件变量

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
using namespace std;
class Foo
{
public:
    Foo() :flag_(0), thread1_(bind(&Foo::threadFunc1, this)), thread2_(bind(&Foo::threadFunc2, this))
    {
        
    }
    ~Foo()
    {
        thread1_.join();
        thread2_.join();
    }
protected:
private:
    void threadFunc1()//如果我们在线程间共享数据,经常会存在一个线程等待另外一个线程的情况,它们之间存在先后关系。这个与互斥量不同,互斥量是保证多个线程的时候当前只有一个线程访问加锁的代码块,它们之间是不存在先后关系的。
    {
        unique_lock<mutex> ul(mutex_);//由于同时有两个线程需要操作flag变量,所以在读写的时候要加锁,std::unique_lock<std::mutex>会保证构造的时候加锁,离开作用域调用析构的时候解锁,所以不用担心加解锁不匹配。
        while (flag_==0)//线程1需要等到线程2将flag设置为非0才进行打印
        {//while (0 == flag_),必须这样写不能写成if(0 == flag_),因为在多核CPU下会存在虚假唤醒(spurious wakes)的情况。
            cond_.wait(ul);//条件变量在wait的时候会释放锁的,所以其他线程可以继续执行。
        }
        cout << flag_ << endl;
    }
    void threadFunc2()//虽然线程1明显比线程2快些(人为制造等待3秒),但是线程1还是会等待线程2将flag设置为非0后才进行打印的。
    {
        this_thread::sleep_for((chrono::milliseconds(3000)));//为了测试,等待3秒
        unique_lock<mutex> u1(mutex_);
        flag_ = 100;
        cond_.notify_one();
    }
    int flag_;
    mutex mutex_;//mutex和cond必须在thread的前面。原因是:如果线程的定义在前面,线程初始化完成之后立马会执行线程函数,而线程函数里用到了mutex和cond,此时如果mutex和cond还没初始化完成,就会出现内存错误。
    condition_variable cond_;
    thread thread1_;
    thread thread2_;
};
int main(int argc, char* argv[])
{
    Foo f;

    system("pause");
    return 0;
}
<think>好的,我现在需要帮助用户了解C++中条件变量的使用方法。首先,用户已经提供了三个相关的引用资料,我需要仔细阅读这些资料,确保正确理解条件变量的概念和用法。 首先,我需要明确条件变量的基本作用。根据引用[1]和[3],条件变量用于线程同步,与互斥锁配合使用,避免数据竞争和死锁。引用[3]提到,条件变量允许线程以无竞争的方式等待条件发生,这应该是核心点。接下来,用户需要知道具体的使用步骤,所以我应该分步骤解释,并结合代码示例。 然后,引用[2]中提到了不使用条件变量和使用条件变量的区别,这可能是一个关键点。用户可能想知道为什么必须使用条件变量,而不仅仅是互斥锁。所以,在回答中需要解释引入条件变量的必要性,比如避免忙等待,提高效率。 接下来,我需要整理使用条件变量的步骤。根据引用[1]和[3],通常步骤包括:初始化条件变量和互斥锁,线程在条件不满足时等待,另一个线程修改条件后通知等待的线程。代码示例部分,引用[1]可能已经提供了示例,但用户可能需要更详细的代码说明,比如如何声明条件变量,使用wait、notify_one等函数。 同时,需要注意常见的陷阱,比如虚假唤醒,所以在wait使用时应该使用循环检查条件。引用[2]和[3]可能提到了这一点,需要在回答中强调。 另外,用户可能对条件变量的成员函数感兴趣,比如wait、notify_one、notify_all的区别,需要简要说明这些函数的用途。 最后,生成相关问题,需要围绕用户可能的进一步疑问,比如条件变量与互斥锁的关系,如何避免死锁,不同通知方法的区别等。 现在需要组织这些信息,结构清晰,分步骤说明,并给出代码示例。确保使用正确的中文术语,避免技术术语错误,同时引用资料中的关键点,并在适当的位置添加引用标识。</think>### C++线程同步条件变量使用方法 #### 一、条件变量的核心作用 条件变量(`std::condition_variable`)是C++标准库中用于线程同步的关键工具,需与互斥锁(`std::mutex`)配合使用。其核心作用是**允许线程在特定条件不满足时主动挂起,并在条件满足时被唤醒**,避免无意义的循环检查(忙等待)[^3]。 #### 二、使用条件变量的步骤 1. **初始化同步工具** ```cpp #include <mutex> #include <condition_variable> std::mutex mtx; // 互斥锁 std::condition_variable cv; // 条件变量 bool ready = false; // 共享条件 ``` 2. **等待条件的线程** ```cpp void waiting_thread() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{ return ready; }); // 等待ready变为true // 条件满足后执行任务 } ``` - `cv.wait()`会自动释放锁并挂起线程,收到通知后重新获取锁并验证条件[^2]。 3. **修改条件的线程** ```cpp void modifying_thread() { { std::lock_guard<std::mutex> lock(mtx); ready = true; // 修改共享条件 } cv.notify_one(); // 通知一个等待线程 } ``` #### 三、关键注意事项 1. **必须使用循环检查条件** 由于虚假唤醒(spurious wakeup)的存在,`wait()`应搭配循环或谓词表达式: ```cpp cv.wait(lock, []{ return ready; }); // 等价于 while(!ready) cv.wait(lock); ``` 2. **通知方法的选择** - `notify_one()`:唤醒一个等待线程 - `notify_all()`:唤醒所有等待线程(适用于多个消费者场景) 3. **锁的作用域管理** 修改条件时需通过`{}`限定锁的作用域,避免通知时仍持有锁[^1]。 #### 四、完整代码示例 ```cpp #include <iostream> #include <thread> #include <mutex> #include <condition_variable> std::mutex mtx; std::condition_variable cv; bool data_ready = false; void consumer() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{ return data_ready; }); std::cout << "处理数据..." << std::endl; } void producer() { { std::lock_guard<std::mutex> lock(mtx); data_ready = true; } cv.notify_one(); } int main() { std::thread t1(consumer); std::thread t2(producer); t1.join(); t2.join(); return 0; } ``` #### 五、典型应用场景 1. 生产者-消费者模型 2. 任务队列的线程池 3. 事件驱动的异步处理 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值