为自定义库实现lock_guard的正确C++方法

问题描述 投票:0回答:3

哦明智的互联网

我们的两位同事之间陷入了僵局,我们需要您的帮助以正确的 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++ locking std
3个回答
1
投票

我不想使用方法指针。

就我个人而言,我希望尽可能转向 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 方向发展,而不是定制。


1
投票

我会回答你标题中的问题,因为没有其他人回答它。

根据文档

lock_guard
适用于“满足
BasicLockable
要求”的任何类型。 BasicLockable只需要两个方法,
lock()
unlock()

为了使

lock_guard
与自定义库一起使用,您需要将
lock()
unlock()
方法添加到库的互斥类中,或者将其包装在另一个具有
lock()
unlock()
方法的类中。


0
投票

你应该使用那些:

  • 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和更高版本的编译器你不能做内联汇编。

如果使用固有的锁定逻辑,您将遇到的主要问题是互斥锁或自旋锁后面的对象是不可复制的。一旦您复制它或复制受保护的变量,它就会停止被锁定。实现互斥机制的对象也是不可复制的。

© www.soinside.com 2019 - 2024. All rights reserved.