当前位置: 首页 > news >正文

【多线程】阻塞等待(Blocking Wait)(以C++为例)

【多线程】阻塞等待(Blocking Wait)(以C++为例)

本文来自于我关于多线程的系列文章。欢迎阅读、点评与交流
1.【多线程】互斥锁(Mutex)是什么?
2.【多线程】临界区(Critical Section)是什么?
3.【多线程】计算机领域中的各种锁
4.【多线程】信号量(Semaphore)是什么?
5.【多线程】信号量(Semaphore)常见的应用场景
6.【多线程】条件变量(Condition Variable)是什么?
7.【多线程】监视器(Monitor)是什么?
8.【多线程】什么是原子操作(Atomic Operation)?
9.【多线程】竞态条件(race condition)是什么?
10.【多线程】无锁数据结构(Lock-Free Data Structures)是什么?
11.【多线程】线程休眠(Thread Sleep)的底层实现
12.【多线程】多线程的底层实现
13.【多线程】读写锁(Read-Write Lock)是什么?
14.【多线程】死锁(deadlock)
15.【多线程】线程池(Thread Pool)
16.【多线程】忙等待/自旋(Busy Waiting/Spinning)
17.【多线程】阻塞等待(Blocking Wait)(以Java为例)
18.【多线程】阻塞等待(Blocking Wait)(以C++为例)
19.【多线程】屏障(Barrier)
20.【多线程硬件机制】总线锁(Bus Lock)是什么?
21.【多线程硬件机制】缓存锁(Cache Lock)是什么?

阻塞等待(Blocking Wait) 是一个并发编程中的核心概念之一。

1. 什么是阻塞等待?

阻塞等待指的是一个线程在执行过程中,由于某些条件暂时不满足(例如等待I/O操作完成、等待获取锁、等待另一个线程的结果等),而主动或被动地暂停自己的执行,让出CPU资源,进入一种“休眠”状态。直到它所等待的条件被满足后,才会被唤醒,重新进入就绪状态,等待CPU调度继续执行。

简单来说就是:线程停下来,等某个事情发生。

2. 为什么需要阻塞等待?

如果没有阻塞等待机制,线程在条件不满足时只能不停地循环检查(即“忙等待”或“自旋”),这会白白浪费宝贵的CPU时间片。

对比一下:

  • 阻塞等待:

    • 线程:”锁还没释放?那我先睡了,锁释放了记得叫醒我。“
    • 优点: 不占用CPU,节能高效。
    • 缺点: 线程切换会带来一定的上下文切换开销。
  • 忙等待:

    • 线程:”锁还没释放?我查一下…还没…我再查一下…还没…“
    • 优点: 响应及时,一旦条件满足可立即继续。
    • 缺点: 持续占用CPU,浪费资源,可能导致性能问题。

在绝大多数应用场景下,阻塞等待是更优的选择,因为CPU时间是宝贵的,应该留给真正需要计算的线程。

3. 常见的阻塞等待场景(附C++代码示例)

以下是一些在C++中典型的会导致线程阻塞等待的情况。

场景一:互斥锁(std::mutex)

当线程尝试获取已被其他线程持有的互斥锁时,会发生阻塞等待。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>std::mutex g_mutex;void worker(int id) {std::lock_guard<std::mutex> lock(g_mutex); // 尝试获取锁,如果被占用则阻塞等待std::cout << "线程 " << id << " 获取到锁,开始执行" << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作std::cout << "线程 " << id << " 释放锁" << std::endl;// lock_guard析构时自动释放锁
}int main() {std::thread t1(worker, 1);std::thread t2(worker, 2);t1.join();t2.join();return 0;
}

输出:

线程 1 获取到锁,开始执行
(等待约2秒后)
线程 1 释放锁
线程 2 获取到锁,开始执行
线程 2 释放锁
场景二:条件变量(std::condition_variable)

条件变量允许线程在某个条件不满足时主动等待,直到其他线程通知条件发生变化。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>std::mutex g_mutex;
std::condition_variable g_cv;
bool g_ready = false;void waiter() {std::unique_lock<std::mutex> lock(g_mutex);std::cout << "等待线程:检查条件,条件不满足,开始等待" << std::endl;// 等待条件满足(防止虚假唤醒)g_cv.wait(lock, []{ return g_ready; });std::cout << "等待线程:条件满足,继续执行" << std::endl;
}void notifier() {std::this_thread::sleep_for(std::chrono::seconds(1));{std::lock_guard<std::mutex> lock(g_mutex);std::cout << "通知线程:改变条件并通知" << std::endl;g_ready = true;}g_cv.notify_one(); // 唤醒一个等待的线程std::cout << "通知线程:通知已完成" << std::endl;
}int main() {std::thread t1(waiter);std::thread t2(notifier);t1.join();t2.join();return 0;
}
场景三:线程等待(std::thread::join)

主线程等待子线程执行完成。

#include <iostream>
#include <thread>
#include <chrono>void worker() {std::cout << "工作线程开始工作..." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "工作线程完成工作" << std::endl;
}int main() {std::thread t(worker);std::cout << "主线程等待工作线程完成..." << std::endl;t.join(); // 主线程阻塞等待工作线程结束std::cout << "主线程继续执行" << std::endl;return 0;
}
场景四:带超时的等待

C++提供了带超时机制的等待,避免无限期阻塞。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>std::mutex g_mutex;
std::condition_variable g_cv;
bool g_ready = false;void timed_waiter() {std::unique_lock<std::mutex> lock(g_mutex);std::cout << "定时等待线程:开始等待,最多等3秒" << std::endl;auto status = g_cv.wait_for(lock, std::chrono::seconds(3), []{ return g_ready; });if (status) {std::cout << "定时等待线程:条件在超时前满足" << std::endl;} else {std::cout << "定时等待线程:等待超时,条件仍未满足" << std::endl;}
}void slow_notifier() {std::this_thread::sleep_for(std::chrono::seconds(5)); // 5秒后才通知{std::lock_guard<std::mutex> lock(g_mutex);g_ready = true;}g_cv.notify_one();std::cout << "慢速通知线程:通知已发出" << std::endl;
}int main() {std::thread t1(timed_waiter);std::thread t2(slow_notifier);t1.join();t2.join();return 0;
}
场景五:Future和Promise(std::future, std::promise)

用于在线程间传递结果,等待异步操作完成。

#include <iostream>
#include <thread>
#include <future>
#include <chrono>int heavy_computation() {std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "计算完成" << std::endl;return 42;
}int main() {// 异步执行计算std::future<int> result = std::async(std::launch::async, heavy_computation);std::cout << "主线程可以做其他事情..." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "主线程其他事情做完,等待计算结果..." << std::endl;// 阻塞等待并获取结果int value = result.get();std::cout << "计算结果: " << value << std::endl;return 0;
}

4. C++线程状态与阻塞

虽然C++标准没有像Java那样明确定义线程状态,但在实际实现中,线程在阻塞等待时会处于类似的状态:

  • 阻塞在互斥锁上:等待获取锁
  • 等待条件变量:主动暂停执行
  • 等待join:等待其他线程结束
  • 等待future:等待异步操作结果

5. 阻塞等待 vs 忙等待(自旋锁)

#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>// 忙等待实现的自旋锁
class SpinLock {
private:std::atomic<bool> locked{false};
public:void lock() {while (locked.exchange(true, std::memory_order_acquire)) {// 忙等待:持续检查直到锁可用}}void unlock() {locked.store(false, std::memory_order_release);}
};SpinLock spin_lock;
std::mutex normal_mutex;void spin_lock_worker(int id) {spin_lock.lock(); // 忙等待获取锁std::cout << "自旋锁线程 " << id << " 开始" << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));std::cout << "自旋锁线程 " << id << " 结束" << std::endl;spin_lock.unlock();
}void normal_lock_worker(int id) {std::lock_guard<std::mutex> lock(normal_mutex); // 阻塞等待获取锁std::cout << "普通锁线程 " << id << " 开始" << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));std::cout << "普通锁线程 " << id << " 结束" << std::endl;
}

总结

特性C++阻塞等待C++忙等待
CPU占用不占用CPU持续占用CPU
实现方式std::mutex, std::condition_variable, future::get()自旋锁,原子变量循环检查
响应速度较慢(需要线程切换)很快(立即响应)
适用场景大多数高并发场景,I/O操作极短等待,低竞争场景

C++提供了丰富的同步机制来实现阻塞等待,正确使用这些机制可以编写出高效、安全的并发程序。选择阻塞等待还是忙等待取决于具体的性能要求和应用场景。

http://www.dtcms.com/a/474410.html

相关文章:

  • c语言动态内存管理
  • 传媒大气的网站网页设计与制作广东开放大学
  • AI 改变数据库产品实践探索
  • 做企业的网站都要准备什么怎么导出wordpress 整个网站
  • 做博客网站赚钱wordpress论坛社区主题
  • 零代码AI开发:Coze平台
  • Docker版本
  • 基于 Docker 的 MongoDB 部署与使用指南
  • 长沙做网站公司杭州seo外包
  • 南山区搜索引擎优化seo多少钱
  • 联邦学习论文分享:Data-centric Federated Graph Learning with Large LanguageModels
  • 【MySQL】MySQL主从复制原理解析:从二进制日志到数据一致性
  • 数据库设计_理论部分_需求分析
  • 做网站的如何增加电话量2020网络营销推广方式
  • 【人工智能】一文解读什么是ACN,ACN新在哪里?(附思维导图总结)
  • 语言实现基本区块链模型
  • 网站用哪种语言手机照片制作成相册
  • ipad mini第一代现在能做什么
  • 美妆企业网站模板网站推广计划书怎么写
  • openharmony之location位置服务模块核心功能解析与实现原理解读
  • 端口复用技术详解与应用场景
  • 6.1中断的概念
  • 学员作业:探索锁屏、桌面壁纸相关差异显示原理
  • 南通做网站多少钱有专业制作网站的公司吗
  • AI辅助故障自愈:从告警到恢复的4级自动化水平
  • DS题目汇编
  • 前端学习 JavaScript (dom操作)(04)
  • 深圳网站创建公司主页不是wordpress
  • FPGA自学笔记(正点原子ZYNQ7020):2.IP核与组成
  • Ninja 的基本使用方法