为什么 std::lock_guard/std::unique_lock 不使用类型擦除?
Why doesn't std::lock_guard/std::unique_lock use type erasure?
为什么std::lock_guard
和std::unique_lock
需要指定锁类型作为模板参数?
考虑以下选择。首先,在detail
命名空间中,存在类型擦除类(非模板抽象基类和模板派生类):
#include <type_traits>
#include <mutex>
#include <chrono>
#include <iostream>
namespace detail {
struct locker_unlocker_base {
virtual void lock() = 0;
virtual void unlock() = 0;
};
template<class Mutex>
struct locker_unlocker : public locker_unlocker_base {
locker_unlocker(Mutex &m) : m_m{&m} {}
virtual void lock() { m_m->lock(); }
virtual void unlock() { m_m->unlock(); }
Mutex *m_m;
};
}
现在te_lock_guard
,类型擦除锁保护,在构造时简单地放置一个正确类型的对象(没有动态内存分配):
class te_lock_guard {
public:
template<class Mutex>
te_lock_guard(Mutex &m) {
new (&m_buf) detail::locker_unlocker<Mutex>(m);
reinterpret_cast<detail::locker_unlocker_base *>(&m_buf)->lock();
}
~te_lock_guard() {
reinterpret_cast<detail::locker_unlocker_base *>(&m_buf)->unlock();
}
private:
std::aligned_storage<sizeof(detail::locker_unlocker<std::mutex>), alignof(detail::locker_unlocker<std::mutex>)>::type m_buf;
};
我已经检查了性能与标准库的类:
int main() {
constexpr std::size_t num{999999};
{
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
for(size_t i = 0; i < num; ++i) {
std::mutex m;
te_lock_guard l(m);
}
std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now();
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << std::endl;
}
{
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
for(size_t i = 0; i < num; ++i) {
std::mutex m;
std::unique_lock<std::mutex> l(m);
}
std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now();
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << std::endl;
}
}
使用g++和-O3
,没有统计上显著的性能损失。
因为这会使实现复杂化而没有任何显著的好处,并且隐藏了std::lock_guard
和std::unique_lock
在编译时知道它们所保护的锁的类型的事实。
你的解决方案是在构造过程中不会发生类模板参数推导这一事实的变通方法-这将在即将到来的标准中解决。
必须指定锁类型是一个令人讨厌的样板文件,这将在c++ 17 (不仅仅是锁保护)中得到解决,这要归功于构造函数(P0091R3)的模板参数推导建议。
建议(已被接受)允许从构造函数中推断模板参数,从而消除了make_xxx(...)
辅助函数或显式指定编译器应该能够推断的类型名的需要:
// Valid C++17
for(size_t i = 0; i < num; ++i) {
std::mutex m;
std::unique_lock l(m);
}
打开c++ 17…同时,不需要类型擦除。模板函数的实参演绎允许我们使用一个简单的帮助器:
template<class Mutex>
auto make_lock(Mutex& m)
{
return std::unique_lock<Mutex>(m);
}
...
std::mutex m;
std::recursive_mutex m2;
auto lock = make_lock(m);
auto lock2 = make_lock(m2);
相关文章:
- 为什么 c++11 std::lock 和 std::scoped_lock 至少需要 2 个参数?
- 为什么"weak.lock()"返回"nullptr" "auto weak=std::make_shared<int>(42);"的定义?
- DRD 报告"conflicting load" std::mutex::lock 上的错误
- 在 std::tie 中使用 std::weak_ptr::lock()
- std::lock_guard 怎么可能比 std::mutex::lock() 更快?
- std::mutex::lock() 产生奇怪(和不必要的)ASM 代码
- std::lock 仍然导致死锁
- 在任何地方对C++中所有并行线程中的所有锁定和解锁实例使用相同的 std::mutex 和 lock 对象
- 当我调用lock()时,为什么std :: mutex会引发异常
- 使用 std::lock (c++11) 的大量 CPU 负载
- std::lock() equivalent for boost::shared_mutex?
- 为什么在 std::lock 中没有超时支持
- “std::mutex”和“std::lock”是否保证处理器间代码中的内存同步
- std::weak_ptr:lock或shared_ptr构造函数
- std::mutex::lock可以抛出即使一切看起来"good"?
- std::weak_ptr.lock在多线程c++中的语义
- STD::Lock 的友元函数定义和替代提升函数
- 什么是更好的 std::lock_guard<std::mutex> lock(std::mutex mutex_var); 或 std::mutex mutex_var.lock();
- 尝试std::lock[_unique]和std::shared_mutex的线程是否会被调用std::lock_sha
- std::lock_guard<std::mutex> lock(m) 是否有简写?