std::condition_variable
是 C++ 标准库中的一个类,用于同步线程的执行。它通常与 std::unique_lock
或 std::lock_guard
配合使用,以及一个条件(通常是 std::condition_variable
的成员变量),以允许线程等待某个条件成立。
下面是 std::condition_variable
的主要功能和方法的详解:
-
构造函数
std::condition_variable()
:默认构造函数。
-
等待方法
void wait(std::unique_lock<std::mutex>& lock)
: 使当前线程阻塞,直到另一个线程调用notify_one
或notify_all
。在等待期间,lock
会被自动释放,当线程被唤醒时,lock
会被重新锁定。template< class Predicate > bool wait(std::unique_lock<std::mutex>& lock, Predicate pred)
: 除了等待通知外,还检查pred
是否为true
。如果pred
为false
,则线程会阻塞,直到pred
为true
或收到通知。void wait_for(std::unique_lock<std::mutex>& lock, std::chrono::duration<Rep,Period> timeout)
: 与wait
类似,但有一个超时时间。如果在超时时间内没有收到通知,则线程会停止阻塞。template< class Rep, class Period, class Predicate > std::cv_status wait_for(std::unique_lock<std::mutex>& lock, std::chrono::duration<Rep,Period> timeout, Predicate pred)
: 结合了wait_for
和带谓词的wait
的功能。void wait_until(std::unique_lock<std::mutex>& lock, std::chrono::time_point<Clock,Duration> timeout)
: 与wait_for
类似,但使用时间点而不是持续时间作为超时。template< class Clock, class Duration, class Predicate > std::cv_status wait_until(std::unique_lock<std::mutex>& lock, std::chrono::time_point<Clock,Duration> timeout, Predicate pred)
: 结合了wait_until
和带谓词的wait
的功能。
-
通知方法
void notify_one()
: 唤醒等待在std::condition_variable
上的一个线程(如果有的话)。void notify_all()
: 唤醒等待在std::condition_variable
上的所有线程。
使用场景:
假设你有多个线程,其中一个线程负责产生数据(生产者),而其他线程负责消费这些数据(消费者)。当生产者产生数据时,它可能会设置一个条件变量来通知消费者线程数据已经准备好。消费者线程则会在循环中等待这个条件变量,直到数据准备好。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::queue<int> dataQueue;
std::mutex mtx;
std::condition_variable condVar;
void producer() {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
dataQueue.push(i);
lock.unlock();
condVar.notify_one();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
condVar.wait(lock, [] { return !dataQueue.empty(); });
int data = dataQueue.front();
dataQueue.pop();
lock.unlock();
std::cout << "Consumed: " << data << std::endl;
}
}
int main() {
std::thread prod(producer);
std::thread cons(consumer);
prod.join();
cons.detach();
return 0;
}
在这个例子中,生产者线程将整数推入队列,并通过 notify_one
通知消费者线程。消费者线程则在一个循环中等待数据,直到数据队列不为空。注意,当调用 wait
时,锁会自动释放,当线程被唤醒时,锁会重新被锁定。这是为了避免死锁并确保线程安全。
举例说明`std::condition_variable`的使用场景
std::condition_variable
的使用场景通常涉及到多个线程之间的同步,特别是在生产者-消费者模型、任务队列、多线程资源共享等场景中。以下是几个具体的例子来说明 std::condition_variable
的使用场景:
1. 生产者-消费者模型
在生产者-消费者模型中,生产者线程生成数据项并将其放入缓冲区(如队列),而消费者线程从缓冲区中取出数据项进行处理。std::condition_variable
可以用来同步生产者和消费者线程,确保消费者线程不会在缓冲区为空时尝试读取数据,而生产者线程也不会在缓冲区满时尝试写入数据。
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
std::queue<int> queue;
std::mutex mtx;
std::condition_variable cond_var;
void producer() {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
queue.push(i);
lock.unlock();
cond_var.notify_one(); // 通知一个等待的消费者线程
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cond_var.wait(lock, [] { return !queue.empty(); }); // 等待队列非空
int value = queue.front();
queue.pop();
lock.unlock();
std::cout << "Consumed: " << value << std::endl;
}
}
int main() {
std::thread producer_thread(producer);
std::thread consumer_thread(consumer);
producer_thread.join();
consumer_thread.detach(); // 消费者线程通常持续运行
return 0;
}
2. 任务队列
在多线程环境中,任务队列通常用于分配工作给不同的线程。当新的任务添加到队列时,需要通知等待任务的线程。std::condition_variable
可以用来实现这一通知机制。
#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
std::queue<std::function<void()>> task_queue;
std::mutex mtx;
std::condition_variable cond_var;
bool stop = false;
void worker_thread() {
while (!stop) {
std::unique_lock<std::mutex> lock(mtx);
cond_var.wait(lock, [] { return !task_queue.empty() || stop; }); // 等待任务或停止信号
if (stop && task_queue.empty()) {
break; // 如果收到停止信号且队列为空,则退出循环
}
std::function<void()> task = std::move(task_queue.front());
task_queue.pop();
lock.unlock();
task(); // 执行任务
}
}
int main() {
std::vector<std::thread> workers;
for (int i = 0; i < 4; ++i) { // 创建四个工作线程
workers.emplace_back(worker_thread);
}
// 添加任务到队列
{
std::lock_guard<std::mutex> lock(mtx);
task_queue.push([] { std::cout << "Task 1 executed\n"; });
task_queue.push([] { std::cout << "Task 2 executed\n"; });
// ... 添加更多任务 ...
}
cond_var.notify_all(); // 通知所有等待的线程有任务可用
// ... 在某个时刻,我们想要停止所有工作线程 ...
{
std::lock_guard<std::mutex> lock(mtx);
stop = true;
}
cond_var.notify_all(); // 通知所有线程停止工作
for (auto& worker : workers) {
worker.join();
}
return 0;
}
3. 多线程资源共享
当多个线程需要访问和修改共享资源时,std::condition_variable
可以用来确保资源在修改时不会被其他线程访问,以及在资源准备好时被正确通知。