news 2026/10/12 1:37:38

《C++ 并发编程指南》互斥量与锁专题:`<mutex>` 头文件摘要全景解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《C++ 并发编程指南》互斥量与锁专题:`<mutex>` 头文件摘要全景解析
  • 教程
  • 并发编程

【免费下载链接】Cplusplus-Concurrency-In-Practice

A Detailed Cplusplus Concurrency Tutorial 《C++ 并发编程指南》

项目地址:https://gitcode.com/gh_mirrors/cp/Cplusplus-Concurrency-In-Practice
点击查看免费下载

本文是《C++ 并发编程指南》第四章「互斥量与锁」的开篇导读,以仓库文档 4.1<mutex>头文件摘要 为核心骨架,对 C++11 标准<mutex>头文件中声明的全部组件做一次全景梳理:四种互斥量(std::mutex、std::recursive_mutex、std::timed_mutex、std::recursive_timed_mutex)、两种 RAII 锁类型(std::lock_guard、std::unique_lock)、三种 Tag 标记类型、std::once_flag,以及std::lock、std::try_lock、std::call_once三个辅助函数,并逐一给出标准类摘要(class synopsis)。读完本文,你将建立起 C++11 互斥量与锁的完整类型地图,清楚每一种类型「能做什么、不能做什么、在什么场景下选用」,为后续阅读互斥量详解、锁类型详解并动手编写多线程同步代码打好基础。

1. 为什么必须包含<mutex>头文件

Mutex 又称互斥量。C++11 中与 Mutex 相关的类(包括锁类型)和函数都声明在<mutex>头文件中,所以如果你在程序里面使用std::mutex及相关类型和辅助函数,就必须包含<mutex>头文件:

#include <mutex>

这一条和std::thread需要包含<thread>、std::atomic需要包含<atomic>是同一套组织方式:C++11 将并发设施按头文件分门别类。以本仓库 code/chapter1/hello-world/hello-world.cc 为例,它使用std::thread时同样在开头包含了<thread>头文件。凡是要在代码里使用互斥量上锁/解锁、用 RAII 锁管理临界区、或者用call_once保证函数只被调用一次,第一步都是引入<mutex>。

2. C++11 中定义的与互斥量和锁相关的类总览

C++11 标准在<mutex>头文件中定义了几大类与互斥量和锁相关的组件,下面先建立整体认知,第 3、4 节再给出精确的声明摘要。

2.1 Mutex 系列类(四种)

C++11 标准中规定的与互斥量相关的类共有四种:

  1. std::mutex:最基本的 Mutex 类,提供了最基本的上锁和解锁操作。基本的互斥量不允许某个线程在已获得互斥量的情况下重复对该互斥量进行上锁操作,所以重复上锁将会导致死锁(结果通常是未定义的)。
  2. std::recursive_mutex:递归 Mutex 类,与std::mutex功能基本相同,但是允许互斥量的拥有者(通常是某个线程)重复对该互斥量进行上锁操作而不会产生死锁,但必须保证上锁和解锁的次数相同。
  3. std::timed_mutex:定时 Mutex 类,与std::mutex功能基本相同,但是提供了两个额外的定时上锁操作try_lock_for和try_lock_until。即某个线程在规定的时间内对互斥量进行上锁操作:如果在规定的时间内获得了锁则返回true,超时则返回false。
  4. std::recursive_timed_mutex:定时递归 Mutex 类,既提供了重复上锁功能,又提供了定时上锁的特性(即在规定的时间内没有获得锁则返回false),相当于std::recursive_mutex和std::timed_mutex的组合。

说明:标准库类型名以std::timed_mutex为准(部分资料中也写作std::time_mutex),下文统一使用标准名std::timed_mutex。

四种互斥量之间的能力差异可以归纳为一张对比表:

类型重复上锁(递归)定时上锁(try_lock_for / try_lock_until)典型场景
std::mutex不支持(重复上锁导致死锁)不支持通用临界区保护
std::recursive_mutex支持(lock/unlock 次数需配对)不支持递归函数内多次获取同一把锁
std::timed_mutex不支持支持需要限时等待锁、避免无限阻塞
std::recursive_timed_mutex支持支持既递归又限时的复合场景

try_lock_for与try_lock_until之间存在着细微差异:前者接收一个相对时间长度(chrono::duration),表示「从调用时刻起,在这段时间内尝试获得锁」;后者接收一个绝对时间点(chrono::time_point),表示「在指定时间点到来之前尝试获得锁」。std::timed_mutex类摘要中两者的模板签名也印证了这一点——try_lock_for的参数类型是chrono::duration<Rep, Period>,try_lock_until的参数类型是chrono::time_point<Clock, Duration>。

2.2 Lock 类(两种)

C++11 标准中定义了两种与互斥量相关的 RAII 技术。RAII 即 Resource Acquisition Is Initialization(资源获取即初始化):把资源的获取与对象的生命周期绑定,对象构造时获取资源、对象析构时释放资源,从而保证资源(这里指锁)在异常路径下也能被正确释放。

  1. std::lock_guard:与 Mutex RAII 相关,方便线程对互斥量上锁。
  2. std::unique_lock:与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

lock_guard的特点是简单安全;unique_lock则在 RAII 的基础上提供了更灵活的控制(延迟上锁、定时上锁、手动解锁、移动所有权、release()等)。两者的详细用法与构造函数分析见仓库 4.3 锁类型详解。

2.3 其他类型

<mutex>头文件中还包含以下辅助类型:

  1. std::once_flag:call_once辅助函数会使用到该类型的对象。
  2. std::adopt_lock_t:一个空的标记类,定义如下:struct adopt_lock_t {};。该类型的常量对象adopt_lock(adopt_lock是一个常量对象,定义如下:constexpr adopt_lock_t adopt_lock {};,constexpr是 C++11 中的新关键字)通常作为参数传入给unique_lock或lock_guard的构造函数,表示「互斥量已经被当前线程锁住,请接管(adopt)它」。
  3. std::defer_lock_t:一个空的标记类,定义如下:struct defer_lock_t {};。该类型的常量对象defer_lock(定义如下:constexpr defer_lock_t defer_lock {};)通常作为参数传入给unique_lock或lock_guard的构造函数,表示「先不立即上锁,推迟(defer)上锁时机」。
  4. std::try_to_lock_t:一个空的标记类,定义如下:struct try_to_lock_t {};。该类型的常量对象try_to_lock(定义如下:constexpr try_to_lock_t try_to_lock {};)通常作为参数传入给unique_lock或lock_guard的构造函数,表示「尝试(try)上锁,失败时不阻塞」。

这三个 Tag 类型都是「空的标记类」,它们的存在价值不是携带数据,而是通过「重载选择」告诉锁类型的构造函数以何种策略管理互斥量。adopt_lock、defer_lock、try_to_lock是 C++11 标准库预定义的constexpr常量对象,可以直接作为实参传入。

2.4 辅助函数

<mutex>头文件中还声明了三个自由函数(非成员函数):

  1. std::try_lock:尝试同时对多个互斥量上锁。
  2. std::lock:同时对多个互斥量上锁。
  3. std::call_once:如果多个线程需要同时调用某个函数,call_once可以保证多个线程对该函数只调用一次。

其中std::lock与std::try_lock用于解决「一次锁定多个互斥量」时的死锁风险:如果分别对两个互斥量逐个lock(),两个线程以相反顺序上锁就可能产生死锁;而std::lock内部会以「同时锁定、失败则回退」的算法避免这一问题(详见仓库 4.3 锁类型详解 中task_a/task_b的示例)。std::call_once与std::once_flag配合使用,是 C++11 中实现「线程安全的懒初始化(lazy initialization)」的推荐手段。

3.<mutex>头文件摘要

本章后面会对以上类型和函数进行详细介绍,我们首先来看<mutex>头文件中各个类型的摘要(synopsis)。所谓「摘要」,是标准中给出的类接口清单——只声明成员函数签名,不包含实现细节,作用是精确刻画「这个类型对外提供哪些操作、哪些操作被禁止」。完整的<mutex>头文件摘要如下:

namespace std { class mutex; class recursive_mutex; class timed_mutex; class recursive_timed_mutex; struct defer_lock_t { }; struct try_to_lock_t { }; struct adopt_lock_t { }; constexpr defer_lock_t defer_lock { }; constexpr try_to_lock_t try_to_lock { }; constexpr adopt_lock_t adopt_lock { }; template <class Mutex> class lock_guard; template <class Mutex> class unique_lock; template <class Mutex> void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y); template <class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...); template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...); struct once_flag { constexpr once_flag() noexcept; once_flag(const once_flag&) = delete; once_flag& operator=(const once_flag&) = delete; }; template<class Callable, class ...Args> void call_once(once_flag& flag, Callable func, Args&&... args); }

从这份摘要中可以读出几个重要信息:

  • 四种互斥量类(mutex、recursive_mutex、timed_mutex、recursive_timed_mutex)在头文件层面先做前置声明,具体接口在各自类摘要中给出;
  • 三个 Tag 结构与三个constexpr常量对象一一对应:defer_lock_t/defer_lock、try_to_lock_t/try_to_lock、adopt_lock_t/adopt_lock;
  • 两种锁类型都是类模板:template <class Mutex> class lock_guard;与template <class Mutex> class unique_lock;,模板参数Mutex即被管理的互斥量类型;
  • std::swap被特化:为unique_lock<Mutex>提供了非成员swap重载;
  • std::lock/std::try_lock是可变参数模板(class... L3),可以一次处理任意多个锁对象;
  • once_flag不可拷贝:拷贝构造与拷贝赋值均被= delete,并且拥有constexpr默认构造,这保证了once_flag可以作为静态存储期对象使用。

4. 各类摘要详解

4.1std::mutex类摘要

std::mutex是 C++11 中最基本的互斥量,提供独占所有权特性——不支持递归地对std::mutex对象上锁。其类摘要如下:

namespace std { class mutex { public: constexpr mutex(); ~mutex(); mutex(const mutex&) = delete; mutex& operator=(const mutex&) = delete; void lock(); bool try_lock() noexcept; void unlock() noexcept; typedef implementation-defined native_handle_type; native_handle_type native_handle(); }; }

对摘要中每个声明的解读:

  • constexpr mutex();:默认构造函数,新创建的mutex对象处于 unlocked(未上锁)状态;constexpr修饰意味着它可以用于常量表达式上下文,例如全局/静态对象的常量初始化。
  • ~mutex();:析构函数。注意标准要求析构时互斥量必须处于未上锁状态,否则行为未定义。
  • mutex(const mutex&) = delete;与mutex& operator=(const mutex&) = delete;:拷贝构造与拷贝赋值被显式删除。互斥量不能拷贝、不能赋值——这是所有四种互斥量共有的设计,因为「锁的所有权」语义上不可复制。
  • void lock();:上锁。调用线程将锁住该互斥量,调用时可能发生三种情况:(1) 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前该线程一直拥有该锁;(2) 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住;(3) 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
  • bool try_lock() noexcept;:尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。调用时同样可能出现三种情况:(1) 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到调用unlock释放;(2) 如果当前互斥量被其他线程锁住,则当前调用线程返回false,而并不会被阻塞;(3) 如果当前互斥量被当前调用线程锁住,则会产生死锁。
  • void unlock() noexcept;:解锁,释放对互斥量的所有权。
  • typedef implementation-defined native_handle_type;与native_handle():返回由实现定义的「原生句柄」类型。在不同平台上它对应底层线程库的互斥量句柄(例如 POSIX 平台上的pthread_mutex_t*),用于与平台特定的底层 API 交互。

std::mutex与 POSIXpthread_mutex_t的编程接口差异,仓库在 4.5 小节 中有专题对比。

4.2std::recursive_mutex类摘要

std::recursive_mutex与std::mutex一样,也是一种可以被上锁的对象;区别在于它允许同一个线程对互斥量多次上锁(即递归上锁),从而获得对互斥量对象的多层所有权。释放互斥量时需要调用与该锁层次深度相同次数的unlock(),可理解为lock()次数和unlock()次数相同。除此之外,std::recursive_mutex的特性和std::mutex大致相同。其类摘要如下:

namespace std { class recursive_mutex { public: recursive_mutex(); ~recursive_mutex(); recursive_mutex(const recursive_mutex&) = delete; recursive_mutex& operator=(const recursive_mutex&) = delete; void lock(); bool try_lock() noexcept; void unlock() noexcept; typedef implementation-defined native_handle_type; native_handle_type native_handle(); }; }

对比std::mutex的摘要可以看到,recursive_mutex的接口形态几乎完全一致(lock/try_lock/unlock/native_handle),唯一的显著差异是默认构造函数不再是constexpr。语义层面的差异(允许递归上锁)由标准库实现内部维护「锁层次深度」来保证,并不反映在接口签名上。

4.3std::timed_mutex类摘要

std::timed_mutex比std::mutex多了两个成员函数:try_lock_for()和try_lock_until()。其类摘要如下:

namespace std { class timed_mutex { public: timed_mutex(); ~timed_mutex(); timed_mutex(const timed_mutex&) = delete; timed_mutex& operator=(const timed_mutex&) = delete; void lock(); bool try_lock(); template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) noexcept; template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept; void unlock(); typedef implementation-defined native_handle_type; native_handle_type native_handle(); }; }

两个新增成员函数的语义:

  • try_lock_for(rel_time):接受一个时间范围(chrono::duration),表示在这段时间范围之内线程如果没有获得锁则被阻塞住。这与std::mutex的try_lock()不同——try_lock如果被调用时没有获得锁则直接返回false,而try_lock_for会阻塞等待一段时间。如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁;如果超时(即在指定时间内还是没有获得锁),则返回false。
  • try_lock_until(abs_time):接受一个时间点(chrono::time_point)作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住。如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁;如果超时(即在指定时间内还是没有获得锁),则返回false。

二者的差异总结:try_lock_for用「相对时长」表达等待上限,适合「等 200 毫秒」这类需求;try_lock_until用「绝对时间点」表达截止时刻,适合「等到某个时刻为止」这类需求(例如与steady_clock::now()计算出的截止点配合)。注意timed_mutex::try_lock()与try_lock_for/try_lock_until均声明为noexcept,而上锁失败时lock()会抛出system_error。

4.4std::recursive_timed_mutex类摘要

和std::recursive_mutex与std::mutex的关系一样,std::recursive_timed_mutex的特性可以从std::timed_mutex推导出来:它同时具备「递归上锁」与「定时上锁」两种能力,相当于std::recursive_mutex与std::timed_mutex的组合。其类摘要如下:

namespace std { class recursive_timed_mutex { public: recursive_timed_mutex(); ~recursive_timed_mutex(); recursive_timed_mutex(const recursive_timed_mutex&) = delete; recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete; void lock(); bool try_lock(); template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) noexcept; template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept; void unlock(); typedef implementation-defined native_handle_type; native_handle_type native_handle(); }; }

接口形态与std::timed_mutex完全一致(lock/try_lock/try_lock_for/try_lock_until/unlock/native_handle),语义上额外叠加了递归特性——同一线程可多次上锁,且解锁次数必须与上锁次数匹配。

4.5std::lock_guard类摘要

std::lock_guard是 C++11 中定义的模板类(template <class Mutex> class lock_guard;)。lock_guard对象通常用于管理某个锁(Lock)对象,与 Mutex RAII 相关,方便线程对互斥量上锁:在某个lock_guard对象的生命周期内,它所管理的锁对象会一直保持上锁状态;而lock_guard的生命周期结束之后,它所管理的锁对象会被解锁(类似shared_ptr等智能指针管理动态分配的内存资源)。lock_guard对象并不负责管理 Mutex 对象的生命周期,它只简化上锁与解锁操作。其类摘要如下:

namespace std { template <class Mutex> class lock_guard { public: typedef Mutex mutex_type; explicit lock_guard(mutex_type& m); lock_guard(mutex_type& m, adopt_lock_t) noexcept; ~lock_guard(); lock_guard(lock_guard const&) = delete; lock_guard& operator=(lock_guard const&) = delete; private: mutex_type& pm; // exposition only }; }

摘要解读:

  • typedef Mutex mutex_type;:将模板参数Mutex暴露为嵌套类型mutex_type,便于泛型代码引用。
  • explicit lock_guard(mutex_type& m);:locking 构造。lock_guard对象管理 Mutex 对象m,并在构造时对m进行上锁(调用m.lock())。
  • lock_guard(mutex_type& m, adopt_lock_t) noexcept;:adopting 构造。lock_guard对象管理 Mutex 对象m,与 locking 构造不同的是,此时 Mutex 对象m已被当前线程锁住(通过std::adopt_lock标记告知构造函数「锁已经拿到,直接接管」)。典型的配合写法是先mtx.lock(),再std::lock_guard<std::mutex> lck(mtx, std::adopt_lock);。
  • ~lock_guard();:析构时自动对它所管理的 Mutex 对象调用unlock()。正是这一行为保证了「异常抛出时锁也能被正确释放」——局部lock_guard对象在栈展开(stack unwinding)过程中析构,锁随之释放。
  • 拷贝构造与拷贝赋值均被= delete:lock_guard对象不可拷贝、不可移动,所有权不可转让。
  • 私有成员mutex_type& pm;标注为// exposition only:它只是「说明性成员」,标准并不要求实现真正拥有该数据成员,只要求行为等价。

模板参数Mutex代表互斥量类型(如std::mutex),它应该是一个基本的BasicLockable类型:只需满足两种操作lock和unlock。标准库中std::mutex、std::recursive_mutex、std::timed_mutex、std::recursive_timed_mutex以及std::unique_lock均满足BasicLockable。

4.6std::unique_lock类摘要

lock_guard最大的特点是安全简单,但最大的缺点也是简单——没有给程序员提供足够的灵活度。因此 C++11 标准中定义了另外一个与 Mutex RAII 相关的类unique_lock:它同样方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。顾名思义,unique_lock对象以独占所有权的方式(unique ownership)管理 mutex 对象的上锁和解锁操作——所谓独占所有权,就是没有其他的unique_lock对象同时拥有某个 mutex 对象的所有权。其类摘要如下:

namespace std { template <class Mutex> class unique_lock { public: typedef Mutex mutex_type; // 构造/拷贝/析构: unique_lock() noexcept; explicit unique_lock(mutex_type& m); unique_lock(mutex_type& m, defer_lock_t) noexcept; unique_lock(mutex_type& m, try_to_lock_t) noexcept; unique_lock(mutex_type& m, adopt_lock_t) noexcept; template <class Clock, class Duration> unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) noexcept; template <class Rep, class Period> unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) noexcept; ~unique_lock(); unique_lock(unique_lock const&) = delete; unique_lock& operator=(unique_lock const&) = delete; unique_lock(unique_lock&& u) noexcept; unique_lock& operator=(unique_lock&& u) noexcept; // 上锁操作: void lock(); bool try_lock(); template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time); template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time); void unlock(); // 修改操作 void swap(unique_lock& u) noexcept; mutex_type *release() noexcept; // observers: bool owns_lock() const noexcept; explicit operator bool () const noexcept; mutex_type* mutex() const noexcept; private: mutex_type *pm; // exposition only bool owns; // exposition only }; template <class Mutex> void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept; }

摘要解读——unique_lock相比lock_guard的灵活性体现在三个层面:

  1. 构造方式多样:除了默认构造和 locking 构造,还提供了四种带 Tag/时间参数的构造——defer_lock_t(延迟上锁,构造时不锁)、try_to_lock_t(尝试上锁,失败不阻塞)、adopt_lock_t(接管已持有的锁)、chrono::duration/chrono::time_point(定时上锁)。默认构造的unique_lock不管理任何 Mutex 对象;通过 adopting 或 locking 构造的对象通常拥有锁;通过 deferred 构造的对象不拥有锁;通过 try-locking 和定时构造的对象则在lock成功时获得锁。
  2. 支持移动语义:拷贝构造与拷贝赋值被删除,但移动构造与移动赋值可用(unique_lock(unique_lock&& u) noexcept等)。移动之后,被移动的对象如同默认构造的一样,不再管理任何 Mutex。
  3. 提供完整的成员函数集:
    • 上锁/解锁操作:lock、try_lock、try_lock_for、try_lock_until、unlock;
    • 修改操作:移动赋值、swap(与另一个unique_lock交换所管理的 Mutex 对象的所有权)、release(返回指向所管理的 Mutex 对象的指针,并释放所有权);
    • 观察操作:owns_lock(返回当前对象是否获得了锁)、operator bool(与owns_lock功能相同)、mutex(返回所管理的 Mutex 对象的指针)。

私有成员pm(mutex_type*)与owns(bool)同样标注为// exposition only:标准只要求行为等价——用指针记录管理的互斥量、用布尔值记录当前是否持有锁。

std::unique_lock与std::lock_guard一样,也保证在其自身析构时它所管理的 Mutex 对象能够被正确解锁(即使没有显式调用unlock函数),因此也是一种简单而安全的上锁和解锁方式,尤其适合在程序抛出异常后保证先前已被上锁的 Mutex 对象被正确解锁。它的模板参数Mutex同样是BasicLockable类型。各类构造函数的逐一分析与完整示例见仓库 4.3 锁类型详解。

5. 从摘要中读出的关键设计

把第 3、4 节的摘要放在一起对照,可以总结出<mutex>头文件的几个贯穿性设计原则:

(1)互斥量不可拷贝、不可赋值。四种互斥量类的拷贝构造与拷贝赋值全部被= delete。这是互斥量语义的必然要求:锁的所有权无法复制,否则将出现「两份所有权」的未定义状态。同理,once_flag也不可拷贝。

(2)RAII 锁类型接管「解锁」责任。lock_guard与unique_lock都在析构时自动解锁,将「手动 lock/unlock 配对」的繁琐且易错的操作,收敛为「对象生命周期即临界区」的模型。两种锁都不负责管理 Mutex 对象的生命周期(Mutex 由用户自行创建和持有),只负责上锁/解锁操作。

(3)Tag 类型驱动「构造策略」。defer_lock、try_to_lock、adopt_lock三个空标记类与常量对象,通过构造函数重载让同一套 RAII 框架支持「延迟上锁」「尝试上锁」「接管已持有的锁」三种策略,这是 C++ 中「空类型 + 重载」这一惯用法的典型体现。

(4)互斥量能力分层:BasicLockable→Lockable→TimedLockable。从摘要可以清晰看到能力递增的三层概念:BasicLockable只需支持lock和unlock(四种互斥量与unique_lock均满足);Lockable在BasicLockable基础上新增try_lock(std::mutex、std::recursive_mutex满足);TimedLockable在Lockable基础上再新增try_lock_for和try_lock_until(std::timed_mutex、std::recursive_timed_mutex满足)。这种分层正是模板算法(如std::lock)能够同时作用于多种锁对象的原因。

(5)constexpr与noexcept的运用。mutex的默认构造函数、三个 Tag 常量对象、once_flag的默认构造函数都是constexpr,支持常量初始化;try_lock、unlock、try_lock_for、try_lock_until等非阻塞类操作声明为noexcept,而可能阻塞或失败的lock()在失败时抛出system_error。这些细节在阅读摘要时值得留意,它们直接影响代码能否用于常量表达式上下文以及异常处理策略。

6. 在仓库环境中编译与验证

理解了类型摘要之后,下一步就是在真实环境中编译运行。本仓库虽然目前只包含一个可直接编译的示例 code/chapter1/hello-world/hello-world.cc(std::thread的 hello world),但它的 Makefile 明确给出了本书全部并发示例的统一编译方式:

CC=g++ CPPFLAGS=-Wall -std=c++11 -ggdb LDFLAGS=-pthread

要点解读:

  • -std=c++11:必须启用 C++11 标准(或更高版本),<mutex>头文件与std::mutex等类型仅在 C++11 及之后的语言标准中可用;
  • -pthread:链接 POSIX 线程库。在 Linux 平台下,凡使用std::thread、std::mutex等并发设施的程序,编译和链接阶段都需要该选项(编译期定义_REENTRANT等宏,链接期引入libpthread);
  • -Wall -ggdb:开启全部警告与调试信息,便于排查并发代码中常见的未使用变量、隐式转换等问题。

参照该 Makefile,编译一个包含<mutex>的程序可以简化为:

g++ -std=c++11 -pthread -o mutex_demo mutex_demo.cc

需要注意的是,Makefile 中的-std=c++11意味着仓库示例面向 C++11 标准编写;如果你的编译环境默认标准更高(如 C++17/C++20),<mutex>头文件中的接口会兼容扩展,但本文描述的类摘要仍以 C++11 为基准。

7. 后续学习路径

本文是第四章的开篇,只完成了「类型地图」的构建。<mutex>头文件中各类的具体用法、完整可运行的代码示例以及更深入的话题,在仓库中按以下顺序展开:

  1. 4.1<mutex>头文件摘要:本文对应的原始文档;
  2. 4.2 互斥量详解:std::mutex的try_lock示例、std::recursive_mutex的递归上锁示例、std::timed_mutex的try_lock_for「烟花(fireworks)」示例等;
  3. 4.3 锁类型详解:lock_guard与unique_lock的全部构造函数、移动赋值、各成员函数(lock/try_lock/try_lock_for/try_lock_until/unlock/release/owns_lock/operator bool/mutex)的逐一分析与示例,以及利用std::lock(foo, bar)同时锁定多把互斥量避免死锁的经典写法;
  4. 本章导读 README.md:第四章整体内容规划;
  5. web-resources.md:与互斥量、锁相关的延伸阅读资料清单。

掌握<mutex>头文件的类型全景之后,建议按「互斥量详解 → 锁类型详解 → 条件变量(第五章)」的顺序继续深入,最终形成完整的 C++11 线程同步知识体系。

  • 教程
  • 并发编程

【免费下载链接】Cplusplus-Concurrency-In-Practice

A Detailed Cplusplus Concurrency Tutorial 《C++ 并发编程指南》

项目地址:https://gitcode.com/gh_mirrors/cp/Cplusplus-Concurrency-In-Practice
点击查看免费下载
上一篇:远程系统资源监控:基于Quasar的实时数据采集技术
下一篇:ECS集群安全更新策略:AMI更新与实例替换最佳实践指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 1:36:07

一条命令让 AI Agent 具备逆向工程能力:REA 快速上手

一条命令让 AI Agent 具备逆向工程能力&#xff1a;REA 快速上手 【免费下载链接】rea Reverse engineer anything with agents, from app behavior down to native binaries. 项目地址: https://gitcode.com/GitHub_Trending/rea2/rea REA&#xff08;Reverse Engineer…

作者头像 李华
网站建设 2026/10/12 1:34:34

从LED看STM32寄存器编程:地址、位运算与外设配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:34:09

AI文档处理:从PDF/Word/Excel到Markdown的转换与优化实践

1. 为什么喂给 AI 的文档格式决定了回答质量的上限很多人用 AI 处理文档时都有过类似的体验&#xff1a;把一份排版精美的 PDF 直接丢进去&#xff0c;问它"帮我总结第三季度的销售数据"&#xff0c;结果 AI 要么答非所问&#xff0c;要么把表格里的数字张冠李戴&…

作者头像 李华
网站建设 2026/10/12 1:34:06

PTA天梯赛L2刷题复盘:模式匹配与数据结构要点

PTA天梯赛的题集&#xff0c;我断断续续刷过一遍之后&#xff0c;前阵子又完整回炉了一遍。这已经是这个系列的第二篇温故笔记&#xff0c;上一篇主要在处理L1的稳定拿分&#xff0c;这次把火力集中在L2区间。为什么单独拎出L2&#xff0c;原因很简单&#xff1a;在天梯赛里L1决…

作者头像 李华
网站建设 2026/10/12 1:34:03

roLabelImg旋转标注工具深度拆解:从theta约定到源码改造

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华