文章目录
- (四)条件变量
(四)条件变量
条件变量(Condition Variable)用于线程间的同步,允许一个或多个线程在特定条件不满足时等待,并在条件满足时被其他线程唤醒。C++标准库中提供了的条件变量类std::condition_variable
,其常用函数有:
-
std::condition_variable()
:默认构造函数 -
void notify_one()
: 唤醒在该条件变量上等待的一个线程(如果有的话)。如果没有线程在等待,那么这个调用就没有效果。 -
void notify_all()
: 唤醒在该条件变量上等待的所有线程。 -
void wait(std::unique_lock<std::mutex>& lock)
: 等待直到另一个线程调用该条件变量的notify_one()
或notify_all()
。
以下例子展示std::condition_variable
的使用:
例子1:使用notify_all
唤醒所有线程
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx; // 全局互斥量
std::condition_variable cv; // 全局条件变量
bool ready = false; // 共享条件
void worker_thread() {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) { // 如果条件不满足,则等待
cv.wait(lck); // 释放锁并等待,直到被唤醒
}
// 当条件满足时,每个线程都执行一些工作(这里只是打印)
std::cout << "Thread " << std::this_thread::get_id() << " is working\n";
}
void go_all() {
std::unique_lock<std::mutex> lck(mtx);
ready = true; // 设置条件为true
cv.notify_all(); // 唤醒所有等待的线程
}
int main() {
std::thread threads[5]; // 创建5个线程
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(worker_thread);
}
std::cout << "5 threads ready to work...\n";
go_all(); // 唤醒所有线程
for (auto& th : threads) {
th.join();
}
return 0;
}
在这个例子中,我们创建了5个线程,每个线程都试图打印其ID。但是,这些线程首先会检查一个共享条件ready
。如果ready
为false
,线程会调用cv.wait(lck)
进入等待状态,并释放它持有的互斥量mtx
。
当go_all
函数中的ready
被设置为true
,并且调用了cv.notify_all()
时,所有等待的线程都会被唤醒,并重新尝试获取互斥锁以继续执行。
注意:std::lock_guard
不提供等待条件变量的机制。因此,在需要使用条件变量(std::condition_variable
)的上下文中,需要使用 std::unique_lock
。
例子2:使用notify_one
唤醒一个线程
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
int task = 0; // 模拟任务,只有一个线程可以处理
void worker_thread() {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) {
cv.wait(lck); // 等待条件成立
}
// 执行任务(这里只是打印)
std::cout << "Thread " << std::this_thread::get_id() << " is processing task " << task << std::endl;
// 任务完成,重置条件
ready = false;
task++; // 下一个任务
}
void go() {
std::unique_lock<std::mutex> lck(mtx);
ready = true; // 设置条件为true
cv.notify_one(); // 唤醒一个等待的线程
}
int main() {
std::thread threads[5]; // 创建5个线程
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(worker_thread);
}
std::cout << "5 threads ready to work...\n";
// 假设有3个任务需要处理
for (int i = 0; i < 3; ++i) {
go(); // 唤醒一个线程处理任务
std::this_thread::sleep_for(std::chrono::seconds(1)); // 假设处理任务需要一些时间
}
for (auto& th : threads) {
th.join();
}
return 0;
}
在这个例子中,每次调用go()
函数时,都会唤醒一个等待的线程来处理任务。由于我们调用了go()
三次,所以只会有三个线程被唤醒并处理任务。