我需要保护对类中数据结构的访问。由于我不能拥有互斥体(因为我无法复制它),我正在考虑拥有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
}
};
我有以下问题:
编辑:也许我需要提供更多细节: 我将仅创建该对象一次并将其保存在 std::vector 中。我不需要复制它,如果向量需要复制,我不想为每个副本都有不同的互斥体。这就是为什么我认为复制构造函数对我有用。
这种方法非常有效和合理,但请注意,随着班级的发展,您可能希望将相同的技术应用于更多班级成员。这就是为什么我建议您考虑利用 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)
{}
如果复制 Sample 对象,将调用复制构造函数,该构造函数可以是由编译器自动生成的,也可以是您显式编写的。
允许复制 Sample 对象是否是一个好主意取决于您想要做什么。 如果允许复制没有意义,则使对象不可复制,例如通过为复制构造函数提供私有原型。
如果您确实想允许复制,那么您需要决定每个副本是否应该有自己的互斥体,并适当地定义复制构造函数。自动生成的复制构造函数只会执行浅复制,因此所有副本将共享互斥体。
您的方法是有效的,但语义可能与您想要实现的目标不同。基本上所有副本都将共享相同的互斥体实例。好消息是,这将保护您免受不同步数据访问的影响,坏消息是它很容易导致互斥锁拥塞。
假设您创建了一个初始实例并复制了多次。他们的数据可能会随着时间的推移而有所不同,但他们仍然共享这个单一的互斥体。如果您现在在多个线程中使用这些副本,它们将全部命中相同的互斥体,并且您的线程将不得不等待很长时间。
按照 Chris Card 的建议 使用 pimpl 模式将会改善情况。在这种情况下,数据和互斥体是集体共享的,因此它们不会分歧。然而,隐式共享/写入时复制可能是危险的,您可能希望避免它。 (C++11 出于某种原因使其变得非法)
不同的方法一旦您了解不需要复制互斥体,修复就很容易了。每个类只接收自己的互斥体实例,该互斥体实例独立于所有其他类。要实现此目的,只需(深度)复制或移动除互斥锁之外的任何内容。然后在构造函数中初始化互斥锁(通常会自动发生)并将其保留在赋值运算符中。
一个例子就讲了1000多个字。我在这里没有使用 Boost,而是使用现代功能,但这可以轻松地通过 Boost 适应 C++98。
在 Coliru 上观看直播您还可以将数据组合到私有嵌套结构及其特殊成员中。然后,面向客户的类可以拥有最少的手动特殊成员,以减少出现错误的机会。