正确的C ++实现自定义库

时间:2017-03-06 06:26:43

标签: c++ locking std

明智的互联网

我们在两位同事之间陷入僵局,我们可以使用您的帮助以正确的C ++方式解决问题。基本上我们有一组实用程序类,其中两个是Mutex和SpinLock类,它们都有以下删节接口:

class Mutex {
public:
    Mutex();
    ~Mutex();
    void Lock();
    void Unlock();
    // ...
};

显然这与std :: lock_guard使用的BasicLockable概念类似但不同,所以我们想要类似的东西(假设Mutex类在这个例子中是不可变的;我们不能向它添加BasicLockable概念) 。此外,并非我们所有支持平台的编译器都是完全c ++ 11特色,所以我们不能只使用vanilla 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类是否有任何内在价值?

3 个答案:

答案 0 :(得分: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_lockT后,使用.lock()的所有代码都可以交换使用.unlock()。这将您的代码库转移到更标准的C ++ 11,而不是定制。

答案 1 :(得分:1)

我会在您的标题中回答这个问题,因为没有人回答。

根据the docslock_guard适用于“满足BasicLockable要求”的任何类型。 BasicLockable仅需要两种方法lock()unlock()

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

答案 2 :(得分: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 ++实现自旋锁或互斥,意味着你需要添加汇编程序或内部代码 - 使它不可移植,除非你为每个平台实现它 - 并且有很多x86-64 C ++ 11及更高版本的编译器,你不能做内联汇编程序。

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