哦明智的互联网
我们的两位同事之间陷入了僵局,我们需要您的帮助以正确的 C++ 方式解决。基本上我们有一组实用程序类,其中两个是 Mutex 和 SpinLock 类,它们都具有以下精简接口:
class Mutex {
public:
Mutex();
~Mutex();
void Lock();
void Unlock();
// ...
};
显然,这与 std::lock_guard 使用的 BasicLockable 概念相似,但大小写不同,因此我们需要类似的东西(假设在此示例中 Mutex 类是不可变的;我们不能向其中添加 BasicLockable 概念)。此外,并非所有支持平台的编译器都具有完整的 c++11 功能,因此我们不能只使用普通的 c++11 支持。
一种思想流派是通用防护类的以下实现,可以继承它以提供通用防护类并从中继承以创建锁防护类:
template<class T, void (T::*EnterFn)(), void (T::*ExitFn)()>
class Guard
{
public: // copy constructor deleting omitted for brevity
Guard( T *lock ) : m_lock(lock) { (m_lock->*EnterFn)(); }
~Guard(); { (m_lock->*ExitFn)(); }
private:
T *m_lock;
};
template<class T>
class LockGuard : public Guard<T, &T::Lock, &T::Unlock>
{
public:
LockGuard(const T* lock) : Guard<T, &T::Lock, &T::Unlock>(lock) {}
};
另一种思想是只实现一个简单的锁卫:
template<class T>
class LockGuard {
T* m_lockable;
public:
LockGuard(const T* lockable) : m_lockable(lockable) { lockable->Lock(); }
~LockGuard() { m_lockable->Unlock(); }
};
您会选择哪种实施方式?为什么?最正确的 C++(03,11,14,17) 实现方式是什么?如上所述,拥有一个通用的 Guard 类是否有任何内在价值?
我不想使用方法指针。
就我个人而言,我希望尽可能转向 C++11 标准工具。 所以我会写一个适配器。
template<class T>
struct lock_adapter {
T* t = nullptr;
void lock() { t->Lock(); }
void unlock() { t->Unlock(); }
lock_adapter( T& tin ):t(std::addressof(tin)) {}
// default some stuff if you like
};
template<class T>
struct adapted_unique_lock:
private lock_adapter<T>,
std::unique_lock< lock_adapter<T> >
{
template<class...Args>
adapted_unique_lock(T& t, Args&&...):
lock_adapter<T>(t),
std::unique_lock< lock_adapter<T> >( *this, std::forward<Args>(args)... )
{}
adapted_unique_lock(adapted_unique_lock&&)=delete; // sadly
friend void swap( adapted_unique_lock&, adapted_unique_lock& ) = delete; // ditto
};
现在
adapted_unique_lock
具有 std::unique_lock
的一组受限功能。
它无法移动,因为
unique_lock
在其实现内部保存着指向 this
的指针,并且不会重新安装它。
请注意,整个
unique_lock
构造函数集的丰富性是可用的。
返回适配唯一锁的函数必须将其返回值存储在类似
auto&&
引用之类的内容中,直到作用域结束,并且在 C++17 之前无法通过链返回它们。
但是,一旦将
adapted_unique_lock
更改为支持 unique_lock
和 T
,任何使用 .lock()
的代码都可以交换为使用 .unlock()
。 这将使您的代码库朝着更标准的 C++11 方向发展,而不是定制。
我会回答你标题中的问题,因为没有其他人回答它。
根据文档,
lock_guard
适用于“满足BasicLockable
要求”的任何类型。 BasicLockable只需要两个方法,lock()
和unlock()
。
为了使
lock_guard
与自定义库一起使用,您需要将 lock()
和 unlock()
方法添加到库的互斥类中,或者将其包装在另一个具有 lock()
和 unlock()
方法的类中。
你应该使用那些:
std::mutex
std::shared_lock
std::unique_lock
std::timed_mutex
std::shared_mutex
std::recursive_mutex
std::shared_timed_mutex
std::recursive_timed_mutex
std::lock_guard
如果您确实有 C++11 编译器。否则会更复杂,应该删除问题上的标签 c++11。
您无法使用 C\C++ 实现自旋锁或互斥体,您需要添加汇编程序或内部代码 - 使其不可移植,除非您为 each 平台实现它 - 并且使用许多 x86-64 C++11和更高版本的编译器你不能做内联汇编。
如果使用固有的锁定逻辑,您将遇到的主要问题是互斥锁或自旋锁后面的对象是不可复制的。一旦您复制它或复制受保护的变量,它就会停止被锁定。实现互斥机制的对象也是不可复制的。