文章大图来源:pixiv_id=52826716
1 std::timed_mutex 基本概念
std::timed_mutex
是自 C++ 11 引入的互斥量(mutex)类型,用于 C++ 多线程编程中控制对共享数据资源的并发互斥访问。std::timed_mutex
是独占且非递归的。
std::timed_mutex
包含了 std::mutex
的基本功能,包括 lock()
和 unlock()
函数进行加锁和解锁、try_lock()
以非阻塞的方式尝试获得锁等。除此以外,std::timed_mutex
还提供了成员函数 try_lock_for()
和 try_lock_until
,用于支持 超时放弃 的尝试获得锁的机制,在避免无限等待、避免出现死锁等方面有着中重要作用。
2 std::timed_mutex::try_lock_for()
std::timed_mutex::try_lock_for()
用于尝试在指定的时间期限内获取互斥量的锁。这个函数接受一个 std::chrono::duration
类型的参数,表示获取锁的最长等待时间。如果在这个时间期限内获得了锁,函数返回 true
,线程继续执行下去;否则,函数返回 false
,线程会采取超时放弃的策略,直接返回。
当 std::timed_mutex::try_lock_for()
接受的参数接近 0
时,那么函数的功能基本就等于 std::timed_mutex::try_lock()
。
这个函数所造成的等待(阻塞)时间可能会略微超过设置的最长等待时间,主要原因在于会有调度或资源争用的延迟。
以下是一个简单的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <iostream> #include <thread> #include <mutex> #include <vector> #include <chrono>
std::timed_mutex t_mtx; int count;
void foo(int id){ std::chrono::milliseconds timeout(100); auto start = std::chrono::steady_clock::now(); if(t_mtx.try_lock_for(timeout)){ auto end = std::chrono::steady_clock::now(); std::chrono::duration<double, std::milli> dura = end - start; std::cout << "thread " << id << ": Successfully get the lock. "; std::cout << "waiting duration = " << dura.count() << "ms." << "\n";
std::this_thread::sleep_for(std::chrono::milliseconds(15)); count ++ ; t_mtx.unlock(); } }
int main(){ std::vector<std::thread> threads(10); for(size_t i = 0; i < threads.size(); i ++ ){ threads[i] = std::thread(foo, i); } for(auto& t : threads) t.join();
std::cout << "final count = " << count << "\n";
return 0; }
|
在上面的代码中,创建了 10
个线程。在线程函数 foo()
中,设置最长等待时间为 100ms
,并且借助了 std::chrono::steady_clock::now()
来进行线程阻塞时长的记录。如果线程成功在等待的时间期限内获得了锁,那么会继续执行下去,模拟一些互斥操作(休息了 15ms
),在作用域结束时手动进行解锁。
可能的运行结果如下:

可以看出,成功获得锁的线程等待时长基本都在期限 100ms
内(也有可能运行出来会有个别线程等待时长略超 100ms
)。在我的电脑上最终能够成功获得锁的线程大致在 5 ~ 7 个,表明每个线程执行函数的时间大致在 20ms
左右(模拟一些互斥操作的 15ms
加上一些其他操作消耗的时间)。
3 std::timed_mutex::try_lock_until()
std::timed_mutex::try_lock_until()
用于尝试在指定时间之前获取互斥量的锁,其接受的是一个 std::chrono::time_point
类型的参数,表示等待获取锁的截止时间。
和 try_lock_for()
一样,如果在这个时间期限内获得了锁,std::timed_mutex::try_lock_until()
函数返回 true
,线程继续执行下去;否则,函数返回 false
,线程会采取超时放弃的策略,直接返回。
以下是一个简单的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <iostream> #include <thread> #include <mutex> #include <vector> #include <chrono>
std::timed_mutex t_mtx; int count;
void foo(int id){ std::chrono::milliseconds timeout(100); auto start = std::chrono::steady_clock::now(); if(t_mtx.try_lock_until(start + timeout)){ auto end = std::chrono::steady_clock::now(); std::chrono::duration<double, std::milli> dura = end - start; std::cout << "thread " << id << ": Successfully get the lock. "; std::cout << "waiting duration = " << dura.count() << "ms." << "\n"; std::this_thread::sleep_for(std::chrono::milliseconds(15)); count ++ ; t_mtx.unlock(); } }
int main(){ std::vector<std::thread> threads(10); for(size_t i = 0; i < threads.size(); i ++ ){ threads[i] = std::thread(foo, i); } for(auto& t : threads) t.join();
std::cout << "final count = " << count << "\n";
return 0; }
|
在上面的代码的线程函数 foo()
中,设置等待的时间截止点为 std::chrono::steady_clock::now()
加上 100ms
的时刻(相当于设置最长等待时间为 100ms
)。并且我们用 std::chrono::steady_clock::now()
来进行线程阻塞时长的记录。如果线程成功在等待的时间期限内获得了锁,那么会继续执行下去,模拟一些互斥操作(休息了 15ms
),在作用域结束时手动进行解锁。
可能的运行结果如下:

参考
-
cppreference.com std::timed_mutex::try_lock_for()
-
cppreference.com std::timed_mutex::try_lock_until()