C++ 超时互斥锁(Timed Mutex):std::timed_mutex 与 std::unique_lock
flyfish
术语对照表
| 中文术语 | 英文术语 | 定义 |
|---|---|---|
| 超时互斥锁 | Timed Mutex | 支持限时获取锁的互斥锁,超时后返回失败而非无限阻塞 |
| 限时等待 | Timed Wait | 在指定时长或时间点内反复尝试获取资源 |
| 非阻塞放弃 | Timeout Fallback | 获取失败后执行替代路径的行为 |
| 延迟加锁 | Deferred Locking | 先持有锁管理对象、后手动触发加锁的策略 |
| 锁所有权 | Lock Ownership | 锁当前归哪个线程持有,由该线程负责解锁 |
一、概述
超时互斥锁(Timed Mutex)在互斥锁的基础上扩展了获取策略:除无限等待的 lock() 外,提供限时等待接口。当等待超时仍未获得锁时,接口返回失败,调用方可以执行替代路径,从而避免线程被永久阻塞。
std::timed_mutex 是 C++ 标准库对超时互斥锁的实现。其限时能力必须由 std::unique_lock 配合 std::defer_lock 与 try_lock_for 才能完整使用。
二、原理
2.1 等待策略的三种形态
| 接口 | 等待上限 | 语义 |
|---|---|---|
| lock() | 无上限 | 无限阻塞,直至获得锁 |
| try_lock() | 零 | 单次尝试,未获得立即返回 false |
| try_lock_for(时长) / try_lock_until(时间点) | 指定时长或时间点 | 限时反复尝试,超时返回 false |
三种形态分别对应无限等待、无等待、限时等待三类需求。
2.2 限时等待的工程价值
无限阻塞的锁存在系统性风险:持锁线程若因异常或逻辑错误长期不释放,所有等待线程将被连带阻塞,程序表现为不可恢复的停滞。限时等待将等待行为置于调用方控制之下:超时后放弃,转而执行替代逻辑或记录错误,阻断故障扩散。
三、接口规范
std::timed_mutex 除继承互斥锁全部接口外,新增:
| 接口 | 参数 | 行为 |
|---|---|---|
| try_lock_for | 时长(std::chrono::duration) | 在指定时长内反复尝试,超时返回 false |
| try_lock_until | 时间点(std::chrono::time_point) | 等待至指定时刻,超时返回 false |
四、使用方式
4.1 不推荐:std::lock_guard 包装超时互斥锁
std::timed_mutex tmtx;voidworker(){std::lock_guard<std::timed_mutex>lock(tmtx);// 合法,但限时能力未启用}std::lock_guard 构造时调用的是 lock(),即无限等待语义。该写法虽可编译,但超时互斥锁的限时能力完全闲置,等价于普通互斥锁。
4.2 正确:std::unique_lock 配合 std::defer_lock
#include<chrono>#include<mutex>std::timed_mutex tmtx;intshared_state=0;booltry_update(){// 阶段一:构造锁管理对象,指定延迟加锁策略,此时未持有锁std::unique_lock<std::timed_mutex>lock(tmtx,std::defer_lock);// 阶段二:限时获取锁if(lock.try_lock_for(std::chrono::seconds(2))){++shared_state;// 已获得锁,临界区returntrue;// 析构时自动解锁}// 阶段三:超时分支,锁管理对象析构时不做任何操作returnfalse;}执行阶段说明:
- 延迟加锁:defer_lock 使 unique_lock 持有锁的管理权但不加锁,为后续选择加锁方式保留余地。
- 限时获取:try_lock_for 在指定时长内反复尝试,获得锁返回 true 并进入临界区,超时返回 false。
- 确定性释放:获得锁时析构自动解锁;未获得锁时析构为空操作。两种终态均无需手动干预。
4.3 多线程竞争示例
#include<chrono>#include<iostream>#include<mutex>#include<thread>std::timed_mutex tmtx;voidworker(intid){std::unique_lock<std::timed_mutex>lock(tmtx,std::defer_lock);if(lock.try_lock_for(std::chrono::milliseconds(300))){std::cout<<"thread "<<id<<" acquired lock\n";std::this_thread::sleep_for(std::chrono::milliseconds(200));}else{std::cout<<"thread "<<id<<" timed out\n";}}intmain(){std::threadt1(worker,1);std::threadt2(worker,2);std::threadt3(worker,3);t1.join();t2.join();t3.join();return0;}输出
thread1acquired lock thread3acquired lock thread2timed out三个线程竞争同一把超时互斥锁,仅首个获得锁;其余线程在限时内未获得锁,转入超时分支执行替代逻辑,全程无线程无限阻塞。