boost::shared_ptr boost::互斥体和复制构造函数

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

我需要保护对类中数据结构的访问。由于我不能拥有互斥体(因为我无法复制它),我正在考虑拥有shared_ptr 并将互斥体保留在那里。这是我的想法的示例代码:

class Sample {
    typedef boost::lock_guard<boost::mutex> AcquireLock;
    boost::shared_ptr<boost::mutex> mutt;

public:
    Sample() : mutt(new boost::mutex) {}

    void Method()
    {
        AcquireLock lock(*mutt);

        //do some work here
    }
};

我有以下问题:

  • 以这种方式使用互斥锁(作为类的成员,通过shared_ptr)是一种不好的做法吗?
  • 我应该为这个类提供复制构造函数吗,因为它通过shared_ptr在堆上分配了内存?

编辑:也许我需要提供更多细节: 我将仅创建该对象一次并将其保存在 std::vector 中。我不需要复制它,如果向量需要复制,我不想为每个副本都有不同的互斥体。这就是为什么我认为复制构造函数对我有用。

c++ boost mutex shared-ptr copy-constructor
3个回答
2
投票

这种方法非常有效和合理,但请注意,随着班级的发展,您可能希望将相同的技术应用于更多班级成员。这就是为什么我建议您考虑利用 pImpl 惯用法:

// in hpp:
class Sample
{
  Impl();
private:
  struct Impl;
  // compiler generated copy-constructor will copy only this shared_ptr
  shared_ptr<void> pImpl_;
};

// in cpp:
struct Sample::Impl
{
  mutex mut_;
  // put here whatever members you need, extend Impl without affecting the Sample interface
};

Impl::Impl() : pImpl_(new Impl)
{}

1
投票

如果复制 Sample 对象,将调用复制构造函数,该构造函数可以是由编译器自动生成的,也可以是您显式编写的。

允许复制 Sample 对象是否是一个好主意取决于您想要做什么。 如果允许复制没有意义,则使对象不可复制,例如通过为复制构造函数提供私有原型。

如果您确实想允许复制,那么您需要决定每个副本是否应该有自己的互斥体,并适当地定义复制构造函数。自动生成的复制构造函数只会执行浅复制,因此所有副本将共享互斥体。


0
投票

是的,但是...

您的方法是有效的,但语义可能与您想要实现的目标不同。基本上所有副本都将共享相同的互斥体实例。好消息是,这将保护您免受不同步数据访问的影响,坏消息是它很容易导致互斥锁拥塞。

假设您创建了一个初始实例并复制了多次。他们的数据可能会随着时间的推移而有所不同,但他们仍然共享这个单一的互斥体。如果您现在在多个线程中使用这些副本,它们将全部命中相同的互斥体,并且您的线程将不得不等待很长时间。

共享的不仅仅是互斥体

按照 Chris Card 的建议 使用 pimpl 模式将会改善情况。在这种情况下,数据和互斥体是集体共享的,因此它们不会分歧。然而,隐式共享/写入时复制可能是危险的,您可能希望避免它。 (C++11 出于某种原因使其变得非法)

不同的方法

所以你的类中需要一个互斥体,你的类必须是可复制的(或者是可移动的,毕竟我是从未来开始写的)并且你显然想避免上面提到的拥塞问题。

一旦您了解不需要复制互斥体,修复就很容易了。每个类只接收自己的互斥体实例,该互斥体实例独立于所有其他类。要实现此目的,只需(深度)复制或移动除互斥锁之外的任何内容。然后在构造函数中初始化互斥锁(通常会自动发生)并将其保留在赋值运算符中。

一个例子就讲了1000多个字。我在这里没有使用 Boost,而是使用现代功能,但这可以轻松地通过 Boost 适应 C++98。

在 Coliru 上观看直播

或在此处查看较短版本以传达想法: std::string

您还可以将数据组合到私有嵌套结构及其特殊成员中。然后,面向客户的类可以拥有最少的手动特殊成员,以减少出现错误的机会。


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