文章目录
多线程开发:wait、sleep、yield全解析
多线程开发中, wait()
、sleep()
和 yield()
的正确使用对于控制线程行为、避免死锁、提升性能至关重要。
1 What
简要介绍
方法 | 属于哪类 | 是否释放锁 | 作用 |
---|---|---|---|
wait() |
同步原语(条件变量) | ✅ 释放锁 | 当前线程等待某个条件满足,挂起执行。 |
sleep() |
时间控制 | ❌ 不释放锁 | 当前线程强制睡眠一段时间。 |
yield() |
调度建议 | ❌ 不释放锁 | 当前线程主动让出 CPU,允许其他线程执行。 |
详细介绍
wait()
— 条件等待(用于线程同步)
std::condition_variable::wait()
(C++11)- Java 的
Object.wait()
也类似
行为:
- 线程在等待某个条件成立。
- 释放所持的互斥锁(mutex),进入阻塞状态。
- 条件满足后,被
notify_one()
/notify_all()
唤醒,重新竞争锁并继续执行。
C++ 示例:
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 释放锁等待条件成立
std::cout << "Condition met, working..." << std::endl;
}
sleep()
— 睡觉,定时挂起
std::this_thread::sleep_for()
std::this_thread::sleep_until()
行为:
- 当前线程被强制阻塞指定时间。
- 不会释放任何锁。
- 时间一到,线程进入就绪状态(可被调度)。
示例:
std::mutex mtx;
void worker() {
std::lock_guard<std::mutex> lock(mtx);
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Wake up after 2 seconds.\n";
}
🔺 注意:如果你在持锁状态下调用 sleep()
,会让其它线程“饿死”或引发死锁。
yield()
— 自愿让出 CPU
std::this_thread::yield()
(C++11)- 实际为对操作系统
sched_yield()
的封装
行为:
- 当前线程主动放弃 CPU 时间片。
- 线程回到就绪队列,允许其他同优先级线程执行。
- 不会阻塞、不休眠、不释放锁。
示例:
void busy_wait() {
while (!ready) {
std::this_thread::yield(); // 避免 CPU 忙等
}
}
2 区别以及建议
区别
特性 | wait() |
sleep() |
yield() |
---|---|---|---|
阻塞线程? | ✅ 是 | ✅ 是 | ❌ 否(可能暂停) |
是否释放锁? | ✅ 是(必须配合 mutex 使用) | ❌ 否 | ❌ 否 |
唤醒条件 | notify_one() / notify_all() |
时间到 | 被 OS 再次调度 |
典型用途 | 线程间同步,条件等待 | 限制频率、延时模拟 | 忙等时避免 CPU 占用 |
所属 API | condition_variable / pthread_cond |
std::this_thread::sleep_for() |
std::this_thread::yield() |
应用场景建议
场景 | 推荐方法 |
---|---|
等待某个状态改变 | wait() |
模拟延时 / 限速 / 轮询间隔 | sleep() |
忙等 / 自旋等待中降低 CPU 占用 | yield() |
3 三者协作使用示例
验证目标:
- 主线程 sleep 控制节奏
- 子线程 yield 等待任务
- 条件变量 wait 同步工作
代码目标:
- 主线程每 1 秒产生一个任务。
- 工作线程使用
yield()
自旋检查是否有任务。 - 一旦任务准备好,工作线程使用
wait()
等待条件变量通知,再执行任务。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <chrono>
std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> ready{false};
int task_counter = 0;
// 工作线程:等待任务并执行
void worker_thread() {
while (true) {
// ⏳ 自旋检查任务是否准备好(减少 wait 的频率)
while (!ready.load()) {
std::this_thread::yield(); // 主动让出 CPU
}
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready.load(); }); // 等待通知,并释放锁
// 模拟任务处理
std::cout << "[Worker] Executing task #" << task_counter << std::endl;
ready = false; // 标记任务完成
if (task_counter >= 5) break; // 结束条件
}
}
// 主线程:周期性产生任务
void task_producer() {
for (int i = 1; i <= 5; ++i) {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟任务生成延迟
{
std::lock_guard<std::mutex> lock(mtx);
task_counter = i;
ready = true;
std::cout << "[Main] Task #" << i << " ready\n";
}
cv.notify_one(); // 通知工作线程
}
}
int main() {
std::thread worker(worker_thread);
std::thread producer(task_producer);
producer.join();
worker.join();
std::cout << "All tasks finished.\n";
return 0;
}
输出:
[Main] Task #1 ready
[Worker] Executing task #1
[Main] Task #2 ready
[Worker] Executing task #2
[Main] Task #3 ready
[Worker] Executing task #3
[Main] Task #4 ready
[Worker] Executing task #4
[Main] Task #5 ready
[Worker] Executing task #5
All tasks finished.
代码行为解析:
主线程:
- 每秒产生一个任务,调用
sleep_for()
。 - 使用锁保护
task_counter
和ready
状态。 - 调用
cv.notify_one()
唤醒工作线程。
- 每秒产生一个任务,调用
工作线程:
- 使用
yield()
忙等(在任务未准备好前避免 CPU 占用)。 - 使用
condition_variable::wait()
挂起,等待任务准备。 - 被通知后执行任务,打印日志,重置状态。
- 使用