如何创建锁定和解锁互斥锁的智能指针?

时间:2013-04-08 10:01:24

标签: c++ thread-safety mutex smart-pointers raii

我有一个线程类,我偶尔会从中获取一个指针实例变量。我希望通过互斥锁保护此访问权限,以便阻止线程访问此资源,直到客户端完成其指针。

我最初的方法是返回一对对象:一个是指向资源的指针,另一个是指向互斥锁上的锁定对象的shared_ptr。此shared_ptr包含对锁对象的唯一引用,因此当超出范围时,应该解锁互斥锁。像这样:

void A::getResource()
{
    Lock* lock = new Lock(&mMutex);
    return pair<Resource*, shared_ptr<Lock> >(
        &mResource, 
        shared_ptr<Lock>(lock));
}

此解决方案不太理想,因为它要求客户端保留整个对象。这样的行为打破了线程的安全性:

Resource* r = a.getResource().first;

另外,我自己的实现是死锁,我很难确定原因,所以可能还有其他问题。

我想要的是一个shared_ptr,它包含锁作为实例变量,并将其与访问资源的方法绑定在一起。这似乎应该有一个既定的设计模式,但做了一些研究,我很惊讶地发现它很难找到。

我的问题是:

  • 这种模式是否有共同的实现?
  • 是否存在将互斥锁置于我忽略的shared_ptr中以防止此模式被广泛传播的问题?
  • 有充分的理由不实现我自己的shared_ptr类来实现这种模式吗?

(注意我正在开发一个使用Qt的代码库但不幸的是在这种情况下不能使用boost。但是,涉及提升的答案仍然是普遍感兴趣的。)

3 个答案:

答案 0 :(得分:7)

我不确定是否有任何标准实现,但由于我喜欢无缘无故重新实现的东西,这里有一个应该有效的版本(假设你不想复制这样的指针):< / p>

template<class T>
class locking_ptr
{
public:
  locking_ptr(T* ptr, mutex* lock)
    : m_ptr(ptr)
    , m_mutex(lock)
  {
    m_mutex->lock();
  }
  ~locking_ptr()
  {
    if (m_mutex)
      m_mutex->unlock();
  }
  locking_ptr(locking_ptr<T>&& ptr)
    : m_ptr(ptr.m_ptr)
    , m_mutex(ptr.m_mutex)
  {
    ptr.m_ptr = nullptr;
    ptr.m_mutex = nullptr;
  }

  T* operator ->()
  {
    return m_ptr;
  }
  T const* operator ->() const
  {
    return m_ptr;
  }
private:
  // disallow copy/assignment
  locking_ptr(locking_ptr<T> const& ptr)
  {
  }
  locking_ptr& operator = (locking_ptr<T> const& ptr)
  {
    return *this;
  }
  T* m_ptr;
  mutex* m_mutex; // whatever implementation you use
};

答案 1 :(得分:5)

您正在描述Kevlin Henney在EXECUTE AROUND POINTER中描述的Executing Around Sequences模式的变体。

我在exec_around.h有一个原型实现,但我不能保证它在所有情况下都能正常工作,因为它正在进行中。它包含一个函数mutex_around,它创建一个对象并将其包装在智能指针中,该指针在访问时锁定和解锁互斥锁。

答案 2 :(得分:0)

这里有另一种方法。远不那么灵活,不那么通用,但也简单得多。虽然它似乎仍然适合您的确切情况。

shared_ptrstandardBoost)提供构建它的方法,同时提供另一个shared_ptr实例,该实例将用于使用计数器和一些不会使用的任意指针完全可以管理。在cppreference.com上,它是第8种形式(the aliasing constructor)。

现在,通常,此表单用于转换 - 例如从派生类对象向基类对象提供shared_ptr。它们共享所有权和使用计数器,但(通常)具有两种不同类型的不同指针值。此表单还用于向shared_ptr基于shared_ptr的成员值提供auto A::getResource() { auto counter = std::make_shared<Lock>(&mMutex); std::shared_ptr<Resource> result{ counter, &mResource }; return result; } ,以对象为其成员。

我们可以在这里滥用&#34;提供锁定防护的形式。这样做:

shared_ptr

返回的mResource指向mMutex,只要被任何人使用,就会锁定mResource

此解决方案的问题在于,您现在有责任确保mMutex保持有效(特别是 - 它不会被销毁)。如果锁定counter足够了,那么你没事。

否则,必须根据您的特定需求调整上述解决方案。例如,您可能希望struct是一个简单的Lock,使shared_ptr和另一个A同时拥有mResource对象拥有{{1} }}