文章目录
- 前言
- 正文
- 等待
- 通知
- 注意事项
- 结尾
前言
条件变量用于多线程中,其作用是在多线程间实现线程的等待、唤醒和通知机制,常配合互斥锁(std::mutex)一起使用。它主要用于解决数据竞争问题>。
正文
条件变量只有五个函数:
方法 | 作用 |
---|---|
notify_one() | 通知一个等待的线程 |
notify_all() | 通知所有等待的线程 |
wait() | 阻塞该线程,直到条件变量被唤醒 |
wait_for() | 阻塞该线程,直到条件变量被唤醒或者到达指定时限时长后 |
wait_until() | 阻塞该线程,直到条件变量被唤醒或者到达指定时间点后 |
条件变量的方法分为两种:通知和等待,我们一个个来说:
等待
wait()部分的就是等待函数,它接收两个参数:
template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);void wait(std::unique_lock<std::mutex>& lock);
它有两个版本,我们先说最简单的版本,它只有一个参数:
void wait(std::unique_lock<std::mutex>& lock);
它接收一个unique_lock作为参数。当程序运行到wait()这一行的时候,程序必定阻塞,只有等到通知之后才会继续运行,这个状态我们也称之为睡眠。
那么有两个参数的呢?它的第二个参数是一个谓词,这里我们能够理解为一个函数,通常是使用lambda表达式。当wait()接到通知的时候,执行这个谓词,若是返回的结果为true,就获取锁的所有权,执行接下来的语句;若是为false,它就重新进入睡眠状态,继续阻塞线程,等待下一次通知的出现。
所以谓词的声明也等同于:
bool pred();
那么其他的wait就不多说了。
通知
通知有两个函数,notify_one()和notify_all(),前者只通知一个线程,而后者则会通知所有线程,在通知之后,被通知的线程会判断是否满足条件函数的要求,若是符合要求,则执行其后面的函数,若是不满足要求,则回到睡眠状态。
注意事项
notify_one() 和 notify_all() 的调用都不会立即执行实际的唤醒操作。相反,它们只是在条件变量上设置了一个唤醒标志,并在互斥锁释放之后,等待其他线程重新获取互斥锁时才会实际执行唤醒操作。
也就是说:只有能获取到互斥锁的时候才会进行唤醒,并让它去争抢互斥锁。
结尾
条件变量的内容其实很少,也比较好理解,问题在怎么去使用它。
这里我给出一段代码,这段代码是一个简单的消息队列,也是个非常简单的生产者消费者队列,实现了一个消息的发送和接收功能,配合代码食用效果更佳(在重要的地方我都写了注释,希望能够帮助大家理解):
#include <iostream>
#include <thread>
#include <memory>
#include <string>
#include <condition_variable>
#include <list>
#include <atomic>std::mutex mtx;
std::condition_variable cv;
std::list<std::string> msg;// 读取数据
void read_thread(){while(true){std::unique_lock<std::mutex> lock(mtx);// 阻塞等待消息(并且解锁)// 有消息再执行,没消息不执行cv.wait(lock,[&](){ return !msg.empty(); });// 获取到互斥锁std::cout << "收到消息,解析中:" << std::endl;std::cout << msg.front() << std::endl;msg.pop_front();}
}// 写入数据
void write_thread(){std::cout << "请输入需要发送的数据:" << std::endl;std::string input;while(true){if(std::cin >> input){std::unique_lock<std::mutex> lock(mtx);msg.push_back(input);std::cout << "数据成功输入" << std::endl;// 通知read线程,有消息可以接收cv.notify_all();}}
}int main(){std::thread write_(write_thread);write_.detach();std::thread read_(read_thread);read_.detach();// 阻塞主线程while(true);return 0;
}