C++并发编程权威报告:std::thread 的机制、演进与架构深度解析
1. 引言:标准化并发的范式转移
在C++11标准发布之前,C++作为一门系统级编程语言,在并发处理方面长期处于一种尴尬的境地。尽管其在底层硬件操作上拥有无与伦比的控制力,但语言标准本身却对“线程”这一概念只字未提。长久以来,开发人员被迫依赖于平台特定的应用程序编程接口(API),如Unix系统上的POSIX线程(pthreads)或Windows操作系统上的Win32 API,来实现多任务处理。这种依赖不仅导致了代码的可移植性严重受损,还使得跨平台的并发抽象层变得异常复杂且容易出错。随着C++11标准的问世,std::thread 库的引入标志着C++进入了一个新的时代。这不仅仅是一个类库的添加,更是一次涉及内存模型(Memory Model)底层的根本性变革,它为多线程执行提供了形式化的定义,确保了在不同硬件架构和编译器优化下的行为确定性。
本报告旨在对 std::thread 进行详尽的解构与分析,不仅涵盖其基础用法与生命周期管理,更将深入探讨其底层的设计哲学、与操作系统内核的交互机制、同步原语的实现细节,以及在现代C++20标准下演进出的 std::jthread 与协作式中断模型。此外,本报告还将从系统架构的高度,剖析线程池设计、异常传播机制以及死锁避免策略,为构建高性能、高可靠性的并发系统提供理论支撑与实践指导。
2. std::thread 的核心机制与构造语义
std::thread 是C++标准库中对执行线程的抽象封装。从微观角度来看,一个 std::thread 对象代表了一个单一的执行流,它直接映射到操作系统内核级的线程资源。然而,与原始的句柄(Handle)不同,std::thread 遵循“资源获取即初始化”(RAII)的设计原则,尽管其在析构行为上的特殊性使得这种RAII特性在C++11中显得并不纯粹。
2.1 构造与参数传递的深层逻辑
std::thread 的构造函数是一个功能强大的变参模板,它允许开发者传递任意的可调用对象(Callable Object)——无论是函数指针、Lambda表达式、仿函数(Functor)还是类的成员函数——以及该调用对象所需的参数。这种设计看似简单,实则隐藏着复杂的类型推导与内存操作机制。当创建一个 std::thread 实例时,C++运行时(Runtime)会启动一个新的操作系统线程,并在该线程的栈空间上执行用户提供的函数5。
参数传递是多线程编程中极易出错的环节。std::thread 的构造函数采用了“衰变拷贝”(Decay Copy)的机制。这意味着,传递给构造函数的参数会首先在调用线程(父线程)的上下文中进行拷贝或移动,存储在线程对象内部的元组中,然后再传递给新创建的执行线程。这种机制的初衷是为了确保线程执行时所访问的数据在其生命周期内是有效的,从而避免悬垂引用(Dangling Reference)的风险。然而,这也意味着默认情况下,所有参数都是按值传递的。如果开发者意图在线程函数中修改父线程的某个对象,或者传递一个无法拷贝的大型对象,直接传递将会导致编译错误或逻辑错误(操作的是副本而非本体)。
为了解决这一问题,C++标准库提供了 std::ref 和 std::cref 包装器。这两个工具通过引用包装器(std::reference_wrapper)将对象的引用语义“伪装”成值语义传递给 std::thread 的构造函数,从而使得新线程能够操作原始对象。这一设计体现了C++在安全性与灵活性之间的权衡:默认拷贝以保证安全,显式引用以提供控制。对于那些仅支持移动操作的资源(如 std::unique_ptr),开发者必须显式使用 std::move 将资源的所有权转移至线程内部。这在构建处理独占资源的后台任务时尤为常见,例如将一个网络套接字的所有权转移给一个专门的处理线程。
2.2 线程标识与句柄管理
每个活动的线程都有一个唯一的标识符,可以通过 get_id() 方法获取。std::thread::id 是一个轻量级的、可复制的、可比较的对象,它不仅用于日志记录和调试,还在死锁检测和资源分配逻辑中扮演关键角色。例如,通过比较当前线程ID与目标线程ID,程序可以避免诸如“线程尝试等待自身结束”(Join Self)这样的逻辑错误,后者在POSIX标准中会导致未定义行为或死锁。
此外,std::thread 还提供了 native_handle() 成员函数,允许访问底层操作系统的原生线程句柄(如 Linux 上的 pthread_t 或 Windows 上的 HANDLE)。这一后门的存在至关重要,因为它允许系统架构师在标准库功能不足时,直接调用操作系统特定的API来设置线程优先级、CPU亲和性(Affinity)或栈大小。这种设计保持了标准库的通用性,同时未牺牲对底层硬件的控制力。
2.3 std::thread 的不可拷贝性与移动语义
std::thread 是典型的“只移型”(Move-only)类型。这一设计决策深刻反映了线程作为系统资源的物理属性:一个执行流在同一时刻只能由一个对象实体所代表。复制一个 std::thread 对象在语义上是模糊的——它究竟意味着创建一个并行运行的克隆线程,还是仅仅复制了控制句柄?为了消除这种歧义并防止资源管理的混乱(如双重释放),C++标准禁止了拷贝构造和拷贝赋值。相反,std::thread 支持移动语义,允许线程的所有权在不同对象、容器或作用域之间安全转移。这使得将 std::thread 放入 std::vector 以管理线程池成为可能,同时也强制开发者必须清晰地界定线程资源的所有权归属。
3. 生命周期管理:可汇合状态与析构陷阱
线程的生命周期管理是C++并发编程中最具挑战性的领域之一。std::thread 对象的状态被定义为“可汇合的”(Joinable)或“不可汇合的”。一个默认构造的、已被移动的、或已经调用过 join() 或 detach() 的线程对象被视为不可汇合。理解这一状态对于编写健壮的代码至关重要,因为 std::thread 的析构函数对可汇合状态有着严苛的检查。
3.1 必须抉择:汇合(Join)还是分离(Detach)
在 std::thread 对象销毁之前,开发者必须对其关联的执行线程做出明确的处置决定:是等待其结束(Join),还是任其独立运行(Detach)。
汇合(Join)是一种同步操作。当父线程调用 t.join() 时,它会被阻塞,直到子线程 t 完成执行。这是一种强一致性的保证,确保了子线程的所有副作用(如内存写入、资源释放)在 join() 返回时对父线程完全可见。这通常用于主线程需要子线程计算结果,或者需要确保子线程使用的栈上资源在销毁前已被释放的场景。忽略 join() 是极其危险的,因为它不仅会导致资源泄漏,还可能引发竞态条件。
分离(Detach)则是一种“发射后不管”(Fire-and-forget)的模式。调用 t.detach() 后,std::thread 对象不再持有底层线程的句柄,该线程转变为守护线程(Daemon Thread),在后台独立运行,直到函数返回。虽然这提供了极大的灵活性,但分离线程是导致程序崩溃和难以调试的Bug的温床。主要风险在于,分离线程可能会访问父线程栈上的局部变量,如果父线程先于分离线程结束,这些变量的内存将被回收,导致分离线程访问非法内存(Use-after-free)。因此,现代C++最佳实践强烈建议谨慎使用 detach(),除非能严格保证线程不依赖任何外部生命周期的资源。
3.2 std::terminate 的达摩克利斯之剑
C++标准委员会在制定C++11时做出了一个极具争议但出于安全考虑的决定:如果一个 std::thread 对象在析构时仍然处于可汇合状态(即既未Join也未Detach),程序将直接调用 std::terminate() 终止运行。这种设计被称为“防御性编程”的极端体现。其逻辑在于,如果程序员忘记了处理线程的生命周期,编译器或运行时不应盲目地替其做决定(隐式Join可能导致UI卡死,隐式Detach可能导致数据损坏),而是应当立即报错以暴露问题。
然而,这一机制与C++的异常处理机制存在严重的冲突。如果在创建线程后的代码块中抛出了异常,栈解退(Stack Unwinding)会触发 std::thread 的析构函数。此时,由于正常的 join() 调用被跳过,析构函数发现线程仍是 Joinable 的,于是触发 std::terminate(),导致程序崩溃,且掩盖了原始异常。为了解决这一问题,开发者必须使用 RAII 包装器(如 std::jthread 或自定义的 scope_guard),确保无论正常退出还是异常退出,线程都能被正确汇合。
4. 同步原语与数据竞争防护
在多线程环境中,共享数据的并发访问是万恶之源。如果至少有一个线程在写入数据,而其他线程在读取或写入,且没有适当的同步措施,就会发生数据竞争(Data Race)。C++标准明确规定,存在数据竞争的程序行为是未定义的(Undefined Behavior)。为了构建定义良好的并发程序,标准库提供了一套丰富的同步原语。
4.1 互斥量(Mutex)的分类与选择
std::mutex 是最基础的同步原语,用于保护临界区(Critical Section)。它保证了同一时刻只有一个线程能持有锁。除了基础的 std::mutex,C++还提供了多种变体以适应不同场景:
- std::recursive_mutex:允许同一线程多次获取同一把锁,这在处理递归算法或复杂的类层次结构调用时非常有用,但会带来额外的性能开销,并可能掩盖设计上的逻辑缺陷。
- std::timed_mutex:支持带有超时机制的锁获取操作(try_lock_for, try_lock_until),这对于避免死锁和在实时系统中防止线程无限期阻塞至关重要。
- std::shared_mutex(C++17):实现了读写锁(Reader-Writer Lock)语义。它允许多个读者同时持有共享锁(Shared Lock),但在写者持有独占锁(Exclusive Lock)时阻塞所有其他访问。这对于读多写少的场景(如配置缓存、路由表查询)能显著提升并发性能。
4.2 锁的RAII管理:从 lock_guard 到 scoped_lock
直接调用 mutex.lock() 和 mutex.unlock() 是极不推荐的,因为如果在临界区内抛出异常或提前返回,将导致锁无法释放,进而引发死锁。C++通过RAII模式完美解决了这一问题。
- std::lock_guard:最轻量级的封装,构造时加锁,析构时解锁。它简单高效,但功能有限,不可移动,不支持手动解锁。
- std::unique_lock:提供了更高级的控制。它支持延迟加锁(std::defer_lock)、手动解锁和重新加锁,并且支持所有权的移动。这种灵活性使其成为配合条件变量使用的唯一选择。
- std::scoped_lock(C++17):这是为了解决死锁问题而引入的现代工具。它是一个变参模板,可以同时对多个互斥量进行加锁。其内部采用了死锁避免算法(通常是 std::lock 的封装),确保无论参数顺序如何,都能安全地获取所有锁。在涉及多个锁的事务操作中,std::scoped_lock 是最佳实践。
4.3 死锁(Deadlock)的解剖与防御
死锁是并发编程中的噩梦,通常发生在两个或多个线程互相等待对方持有的资源时。经典的“ABBA”死锁场景是:线程1持有锁A等待锁B,线程2持有锁B等待锁A。
为了防御死锁,系统设计必须遵循严格的锁获取顺序。例如,强制所有线程按照内存地址的升序获取锁。此外,使用 std::lock 或 std::scoped_lock 可以在库层面自动处理锁的排序,彻底消除因加锁顺序不一致导致的死锁。通过减少锁的粒度(Granularity)和持有时间,也能有效降低死锁发生的概率。
5. 线程间的通信与协调:条件变量
互斥量解决了互斥访问的问题,但无法解决线程间的协调问题。例如,消费者线程需要等待生产者线程生产出数据。如果使用互斥量配合轮询(Spinning),将极大地浪费CPU资源。std::condition_variable 提供了一种机制,允许线程在特定条件满足前进入休眠状态。
5.1 等待与通知机制的深层逻辑
条件变量必须配合 std::unique_lock 使用。当线程调用 wait() 时,它必须持有锁。wait() 操作原子性地释放锁并将线程挂起放入等待队列。当被唤醒时,线程会重新竞争并获取锁,然后继续执行。这种“释放-等待-获取”的原子性是防止“丢失唤醒”(Lost Wakeup)的关键。如果释放锁和进入等待不是原子的,那么在释放锁之后、进入等待之前的间隙,另一个线程可能发送了通知,导致该通知被错过,线程将无限期休眠。
5.2 虚假唤醒(Spurious Wakeup)与谓词保护
在多核系统和POSIX实现中,由于信号处理或调度优化的原因,线程可能会在没有收到 notify 的情况下从 wait 中醒来,这被称为虚假唤醒。此外,即使被正常唤醒,在线程重新获取锁的过程中,共享状态可能已经被其他线程改变(例如队列被其他消费者抢先清空)。因此,等待操作必须总是包含在 while 循环中,反复检查条件是否满足。C++标准库的 wait 函数提供了一个重载版本,接受一个谓词(Predicate),自动处理这一循环逻辑:
cv.wait(lock,{returnis_ready;});这不仅简化了代码,更从根本上杜绝了虚假唤醒带来的逻辑错误22。
5.3 notify_one 与 notify_all 的策略选择
- notify_one:唤醒等待队列中的一个线程。这在资源独占型场景(如单一生产者-单一消费者,或互斥的任务队列)中非常高效,因为它避免了“惊群效应”(Thundering Herd),即唤醒所有线程争抢一个锁,导致大量上下文切换和CPU空转。
- notify_all:唤醒所有等待线程。这适用于状态变更对所有线程都有意义的场景,例如“停止所有工作”的信号,或者读写锁中唤醒所有读者。误用 notify_all 可能会导致严重的性能退化。
5.4 std::condition_variable_any 的通用性
标准的 std::condition_variable 仅支持 std::unique_lock<std::mutex>,这是为了性能优化。但在某些场景下,我们需要配合 std::shared_lock 或自定义锁使用。std::condition_variable_any 提供了这种通用性,它可以与任何满足 BasicLockable 要求的对象配合。特别是在C++20中,它支持与 std::stop_token 集成,实现了可中断的等待,这在实现优雅退出的线程模型时具有不可替代的价值。
6. 现代并发的新篇章:C++20 std::jthread 与协作式中断
C++20 标准引入的 std::jthread(Joining Thread)是对 std::thread 的重大改进,它修复了长期以来被诟病的生命周期安全问题,并标准化了线程中断机制。
6.1 自动汇合的 RAII 语义
std::jthread 是一个真正的 RAII 类型。其析构函数不再调用 std::terminate,而是自动请求线程停止(如果支持)并调用 join()。这意味着,当 std::jthread 对象离开作用域时,它会负责清理关联的线程资源。这一改变彻底消除了因忘记 Join 而导致的程序崩溃风险,使得并发代码的编写更加安全和符合直觉。
6.2 协作式中断模型:stop_token
在C++20之前,停止一个线程通常需要开发者自己实现原子标志(Atomic Flag)或使用条件变量。std::jthread 内置了协作式中断机制,由 std::stop_source、std::stop_token 和 std::stop_callback 组成。
- std::stop_source:用于发起停止请求。
- std::stop_token:传递给线程函数,用于查询是否收到了停止请求(stop_requested())。
- std::stop_callback:允许注册一个回调函数,当停止请求发出时立即执行。这对于唤醒阻塞在 I/O 操作或条件变量上的线程至关重要。
下表对比了 std::thread 与 std::jthread 的关键特性:
| 特性 | std::thread (C++11) | std::jthread (C++20) |
|---|---|---|
| 析构行为 | 若 Joinable 则调用 std::terminate | 发起停止请求并调用 join() |
| 中断支持 | 无(需手动实现) | 内置 std::stop_token |
| 异常安全性 | 低(需 try-catch 保护) | 高(RAII 自动处理) |
| 代码冗余 | 高(需显式 Join/Detach) | 低(自动管理) |
6.3 可中断的等待操作
std::condition_variable_any 在 C++20 中得到了增强,其 wait 系列函数可以接受 std::stop_token。这解决了并发编程中的一个经典难题:如何唤醒一个死等在条件变量上的线程以令其退出?在旧标准中,这往往需要修改谓词逻辑并发送虚假通知。而在 C++20 中,一旦 stop_token 收到停止请求,wait 操作会立即返回,从而实现优雅且响应迅速的线程终止逻辑。
7. 架构思考:线程池与异步任务
尽管 std::thread 提供了强大的底层控制,但在高性能系统架构中,直接使用原生线程往往不是最佳选择。
7.1 线程创建的开销与限制
创建一个操作系统线程并非零成本。它涉及内核对象的分配(TCB)、栈空间的分配(通常默认为数MB)、以及将其加入调度器带来的上下文切换压力。在高并发场景下(如每秒处理数万请求),为每个任务创建一个新线程(Thread-per-request)会导致内存耗尽和调度抖动(Thrashing),严重降低吞吐量。基准测试表明,频繁创建和销毁线程的开销可达数十微秒级,这对于延迟敏感型应用是不可接受的。
7.2 线程池(Thread Pool)的最佳实践
线程池通过复用一组预先创建的线程来执行任务,从而摊薄了线程创建的开销。一个优秀的 C++ 线程池设计通常包含:
- 任务队列:用于存储待执行的函数对象(通常使用 std::function<void()> 封装)。
- 工作窃取(Work Stealing):现代线程池(如 Intel TBB 或 std::execution 的实现)通常为每个线程维护本地队列。当本地队列为空时,线程会从其他线程的队列尾部“窃取”任务。这极大地减少了对全局队列锁的竞争,并提高了CPU缓存的局部性(Cache Locality)。
7.3 std::async 与 std::future
对于一次性的异步计算任务,std::async 提供了比 std::thread 更高层次的抽象。它返回一个 std::future 对象,用于获取计算结果或捕获异常。
- 异常传播:std::thread 无法直接将异常传播回主线程,必须通过 std::exception_ptr 手动捕获和重新抛出。而 std::future::get() 会自动重新抛出在异步任务中捕获的异常,极大地简化了错误处理流程。
- 启动策略:std::async 接受启动策略参数。std::launch::async 强制开启新线程,而 std::launch::deferred 则延迟到调用 get() 时在当前线程执行。需要注意的是,std::async 返回的 future 在析构时会阻塞等待任务完成(如果是 Async 策略),这使得它在某些用法下退化为同步调用,这是开发者常犯的错误之一。
8. 性能优化与硬件亲和性
8.1 伪共享(False Sharing)与缓存一致性
在多核架构下,如果两个线程频繁修改位于同一缓存行(Cache Line,通常为64字节)的不同变量,会导致CPU核心之间频繁进行缓存一致性协议(如MESI)的通信,使缓存行失效,从而严重拖慢性能。这种现象称为伪共享。
C++17 引入了 std::hardware_destructive_interference_size 常量,开发者可以使用 alignas 关键字将频繁修改的数据结构对齐到该尺寸,以确保它们占据独立的缓存行,从而消除伪共享带来的性能损耗。
8.2 硬件并发度
std::thread::hardware_concurrency() 提供了一个提示,告知程序当前系统可用的逻辑核心数。在设计线程池或并行算法时,将线程数设置为该值的倍数(通常是 1x 或 2x,取决于任务是计算密集型还是IO密集型)是通用的启发式规则,能够最大化利用硬件资源而不至于导致过度的上下文切换。
9. 结论
std::thread 及其相关生态系统为C++开发者提供了一套虽然底层但功能完备的并发工具箱。从 C++11 的开创性引入,到 C++17 的算法并行化,再到 C++20 的 jthread 和协作式中断,C++ 的并发模型正朝着更安全、更高效、更易用的方向演进。
对于系统架构师而言,精通 std::thread 的内部机制——包括内存模型、同步原语的实现细节以及操作系统层面的交互——是构建高可靠性系统的基石。然而,在现代应用开发中,应当审慎地使用原生线程,更多地借助于线程池、并行算法库(如 std::execution)以及高层抽象(如 std::async)来管理并发,从而在性能与代码可维护性之间找到最佳的平衡点。掌握这些知识,不仅是为了编写出能运行的代码,更是为了编写出在极端负载下依然稳健、在复杂交互中依然逻辑自洽的卓越软件。
引用的著作
- When should you use std::thread::joinable? - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/42924503/when-should-you-use-stdthreadjoinable
- std::thread::joinable - cppreference.com, 访问时间为 十二月 13, 2025, https://saco-evaluator.org.za/docs/cppreference/en/cpp/thread/thread/joinable.html
- std::condition_variable - cppreference.com, 访问时间为 十二月 13, 2025, http://naipc.uchicago.edu/2015/ref/cppreference/en/cpp/thread/condition_variable.html
- std::thread::thread - cppreference.com, 访问时间为 十二月 13, 2025, https://en.cppreference.com/w/cpp/thread/thread/thread.html
- c++ - When should I use std::thread::detach? - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/22803600/when-should-i-use-stdthreaddetach
- Learn Passing Arguments to Threads | Concurrency Foundations - Codefinity, 访问时间为 十二月 13, 2025, https://codefinity.com/courses/v2/e0a78469-ea8d-43f2-b2e9-029cabf914b3/f925c487-b081-4cac-bea2-247f65c16af1/fba39db0-1520-47b1-a061-fba400e7bf22
- using std::move in std::thread - c++ - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/33895715/using-stdmove-in-stdthread
- Moving an argument into a std::thread? - c++ - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/32443972/moving-an-argument-into-a-stdthread
- Multithreading | Handling Race Conditions and Deadlocks in C++, 访问时间为 十二月 13, 2025, https://www.loadingshaders.xyz/blog/multithreading-handling-race-conditions-and-deadlocks-in-cpp/
- Multithreading | Handling Race Conditions and Deadlocks in C++ - DEV Community, 访问时间为 十二月 13, 2025, https://dev.to/shreyosghosh/multithreading-handling-race-conditions-and-deadlocks-in-c-4ae4
- C++11 std::thread join to main thread in a separate method - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/30151520/c11-stdthread-join-to-main-thread-in-a-separate-method
- detach() and join() in threads in C++ | by Abhishek Jain | Medium, 访问时间为 十二月 13, 2025, https://medium.com/@abhishekjainindore24/detach-and-join-in-threads-in-c-3ee64e411817
- What is std::jthread in c++20? - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/62325679/what-is-stdjthread-in-c20
- The Risks of Mutexes – MC++ BLOG - Modernes C++, 访问时间为 十二月 13, 2025, https://www.modernescpp.com/index.php/the-risk-of-mutexes/
- Simplest way to make std::thread exception safe - c++ - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/33307099/simplest-way-to-make-stdthread-exception-safe
- std::condition_variable - cppreference.com - C++ Reference, 访问时间为 十二月 13, 2025, https://en.cppreference.com/w/cpp/thread/condition_variable.html
- Exploring Inter-thread Communication with Condition Variables | CodeSignal Learn, 访问时间为 十二月 13, 2025, https://codesignal.com/learn/courses/concurrency-essentials-in-cpp/lessons/exploring-inter-thread-communication-with-condition-variables
- C++11: Why does std::condition_variable use std::unique_lock? - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/13099660/c11-why-does-stdcondition-variable-use-stdunique-lock
- deadlock on c++ std::thread even with mutex lock - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/46075532/deadlock-on-c-stdthread-even-with-mutex-lock
- Threads and simple Dead lock cure - c++ - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/1892619/threads-and-simple-dead-lock-cure
- Condition Variables in C++ Multithreading - GeeksforGeeks, 访问时间为 十二月 13, 2025, https://www.geeksforgeeks.org/cpp/cpp-multithreading-condition-variables/
- Spurious wakeups explanation sounds like a bug that just isn’t worth fixing, is that right?, 访问时间为 十二月 13, 2025, https://softwareengineering.stackexchange.com/questions/186842/spurious-wakeups-explanation-sounds-like-a-bug-that-just-isnt-worth-fixing-is
- Condition Variables – MC++ BLOG - Modernes C++, 访问时间为 十二月 13, 2025, https://www.modernescpp.com/index.php/condition-variables/
- c++ 11 condition_variable wait spurious wake up is not working - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/49571276/c-11-condition-variable-wait-spurious-wake-up-is-not-working
- std::condition_variable_any::wait - cppreference.com, 访问时间为 十二月 13, 2025, https://cppreference-45864d.gitlab-pages.liu.se/en/cpp/thread/condition_variable_any/wait.html
- Daily bit(e) of C++ | std::condition_variable, std::condition_variable_any | by Šimon Tóth, 访问时间为 十二月 13, 2025, https://medium.com/@simontoth/daily-bit-e-of-c-std-condition-variable-std-condition-variable-any-4e57564f0341
- std::jthread: A safer and more capable way of concurrency in C++20 | by Erdem Tuzla, 访问时间为 十二月 13, 2025, https://medium.com/bosphorusiss/std-jthread-a-safer-and-more-capable-way-of-concurrency-in-c-20-07121cec3a84
- C++ 20 - concurrency - stop_token in jthread - politely interrupting the thread, 访问时间为 十二月 13, 2025, https://dev.to/sommukhopadhyay/c-20-concurrency-stoptoken-in-jthread-politely-interrupting-the-thread-79j
- Cooperative Interruption of a Thread in C++20 – MC++ BLOG - Modernes C++, 访问时间为 十二月 13, 2025, https://www.modernescpp.com/index.php/cooperative-interruption-of-a-thread-in-c20/
- std::jthread::request_stop - cppreference.com, 访问时间为 十二月 13, 2025, https://cppreference-45864d.gitlab-pages.liu.se/en/cpp/thread/jthread/request_stop.html
- Thread Pool in C++ - GeeksforGeeks, 访问时间为 十二月 13, 2025, https://www.geeksforgeeks.org/cpp/thread-pool-in-cpp/
- A simple and fast C++ thread pool implementation capable of running task graphs - arXiv, 访问时间为 十二月 13, 2025, https://arxiv.org/html/2407.15805v2
- A C++ Thread Pool that can substitute for std::async : r/cpp - Reddit, 访问时间为 十二月 13, 2025, https://www.reddit.com/r/cpp/comments/16gpfqc/a_c_thread_pool_that_can_substitute_for_stdasync/
- Propagate exceptions across threads - Vorbrodt’s C++ Blog, 访问时间为 十二月 13, 2025, https://vorbrodt.blog/2019/03/24/propagate-exceptions-across-threads/
- Exception propagation and std::future - c++ - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/14222899/exception-propagation-and-stdfuture
- C++ : Throw Away threads vs Thread Pool - Stack Overflow, 访问时间为 十二月 13, 2025, https://stackoverflow.com/questions/71442472/c-throw-away-threads-vs-thread-pool