我正在尝试使用互斥锁使类线程安全。
class Container
{
private:
vector<Foo> v;
boost::mutex m;
public:
void add(Foo item)
{
m.lock();
v.push_back(item);
m.unlock();
}
};
问题是boost::mutex
是不可复制的,因此这使得Container
不可复制。当然,如果我复制Container
,新实例可能不需要保留与旧实例相同的互斥锁 - 它可以拥有自己的新互斥锁。我可以为Container
编写自定义复制构造函数,但实际上它是一个复杂的类,我不想这样做。那怎么样:
class CopyableMutex
{
private:
boost::mutex m;
public:
CopyableMutex() {}
CopyableMutex(CopyableMutex&) {} //don't copy, just create a new one
CopyableMutex& operator=(CopyableMutex&) {return *this;} //don't assign, keep it the same
void lock() {m.lock();}
void unlock() {m.unlock();}
};
...然后将boost::mutex
中的Container
替换为CopyableMutex
。
这是一件可怕的事吗?如果没有,那么我是否重新发明轮子 - 是否有一个库类已经这样做了?
答案 0 :(得分:2)
是的,这很可怕。
问题的正确解决方案是容器的自定义复制构造函数和赋值运算符。
如果类太“复杂”而无法编写自定义复制构造函数,那么将线程安全与容器分开,并使基类容器不包含互斥锁,也许还有派生类“线程安全”容器“包含一个互斥锁,并且有一个自定义复制构造函数和赋值操作,只调用自动生成的基类。
答案 1 :(得分:0)
我相信,以这种方式无效的事情之一就是如今使用互斥体的正确方法:
void func()
{
std::lock_guard<std::mutex> lock(mtx);
// do things
}
由于您无法返回互斥锁,因此无法将其扩大到您要使用的位置。使用上述样式的原因是为了防止在使用锁时出现问题,然后在解锁之前发生某种形式的未处理异常(即,它确保了更可靠的解锁)。
不过,我同意其他答案,更好的方法是封装线程安全部分并将其与复杂代码隔离(如果可能),然后为该较小的类创建显式的副本构造函数。