用std::互斥对象复制类
Copy Class with std::mutex
我有一个std::互斥锁作为成员的类。我正在尝试创建一个这样的类数组
class C
{
int x;
std::mutex m;
};
int main()
{
C c[10];
//later trying to create a temp C
C temp = c[0];
}
显然以上是不可能的,因为互斥对象是不可复制的。解决这个问题的方法是通过复制构造函数。
然而,我在创建复制构造函数时有问题。我试过了
C (const C &c)
{
x = c.x;
//1. m
//2. m()
//3. m = c.m
}
我不确定这3个选择中的正确语法是什么。请帮助。
你不应该写这些行。复制构造函数的实现相当于:
C (const C &c) : x(), m()
{
x = c.x;
}
因此互斥锁m
的新实例是默认初始化的,这意味着将调用一个默认构造函数。可以安全使用。
m
保护x
,你应该在访问value:
之前显式地锁定它。C (const C &c)
{
std::lock_guard<std::mutex> guard(c.m);
x = c.x;
}
需要将m
声明为可变的(因为c
是复制函数中的const引用)。
mutable std::mutex m;
最后,您可以看到复制内部带有互斥锁的对象是令人困惑的,如果C
是公共类,它会使它的用户感到困惑,因此在实现复制它之前请三思。
简短的回答你不能复制互斥锁
让我们从基础开始,互斥锁是互斥的简称,也就是说,当有多个线程时,你不希望它们并行地改变/修改值。您需要序列化访问或修改/read,以便值read是有效的。
在上面的例子中,你是在向变量复制一个新值。在这种情况下,您不需要在创建新对象时使用互斥锁。
您可以使用shared_ptr<C>
的数组,然后您不需要C
本身是可复制的…
std::互斥体m不需要复制。您可以使用默认构造的互斥锁
正如在其他答案中所述,只有在相当小的情况下才需要这样做,但是如果您有一些内部使用互斥锁的对象类,则需要使用复制和移动构造函数来显式声明除了互斥锁之外的所有内容来移动和复制。这将导致互斥锁(以及其他任何被遗漏的东西)被默认构造(即每个新的或复制的对象将获得自己唯一的互斥锁)。请确保在使用copy或move构造函数时,无论互斥锁用于保护什么,都不会被调用,因为它们不会(不能?)调用互斥锁进行锁定。
这里有一个完整的例子来帮助任何人在未来遇到这个问题:
class Shape
{
public:
Shape() {} //default constructor
Shape(double _size) //overloaded constructor
{
size = _size;
}
Shape(const Shape& obj) //copy constructor (must be explicitly declared if class has non-copyable member)
{
//Do not put any non-copyable member variables in here (ie the mutex), as they will be
//default initialized if left out
size = obj.size; //any variables you want to retain in the copy
}
Shape& operator=(const Shape&& obj) //move constructor (must be explicitly declared if class has non-copyable member)
{
//Do not put any non-copyable member variables in here (ie the mutex), as they will be
//default initialized if left out
size = obj.size;//any variables you want to retain in the move
return *this;
}
double testMe() { return size; }
private:
std::mutex dataMutex;
double size;
};
以上都是不好的建议,建议你打破零规则。
一个更好的方法是创建一个实用程序类,根据您想要应用的规则来处理复制互斥锁的问题。例如(这可能不符合您的需求)以下代码
https://godbolt.org/z/Y86jscd6K#include <iostream>
#include <variant>
#include <mutex>
struct mutex_holder {
std::mutex mutex;
mutex_holder():mutex(){}
mutex_holder(mutex_holder const & other):mutex(){}
};
演示了一个名为mutex_holder的类型,它遵循复制时总是获得一个新的互斥锁的规则。您可以将此策略应用于需要复制
的类中的互斥锁。struct A {
mutex_holder mHolder;
int x;
int z;
int y;
};
,然后当你需要使用锁
时void Foo(A & a)
{
std::scoped_lock(a.mHolder.mutex);
a.x = a.z + a.y;
}
您可以看到类A是可复制和可移动的,而无需编写任何自定义的特殊成员函数构造函数。
int main(){
A a;
A aa = a;
Foo(a);
Foo(aa);
A aaa = std::move(aa);
Foo(aaa);
}
如果你需要在复制过程中保护一些成员,你也可以实现这样的策略类。
#include <iostream>
#include <variant>
#include <mutex>
template <typename T>
struct mutex_holder {
std::mutex mutex;
mutex_holder():mutex(){}
mutex_holder(mutex_holder const & other):mutex()
{
std::scoped_lock lock(mutex);
{
data = other.data;
}
}
T data;
};
struct A {
struct Inner {
int x;
int z;
int y;
};
mutex_holder<Inner> mHolder;
};
void Foo(A & a)
{
std::scoped_lock(a.mHolder.mutex);
a.mHolder.data.x = a.mHolder.data.z + a.mHolder.data.y;
}
int main(){
A a;
A aa = a;
Foo(a);
Foo(aa);
}
同样,您的业务逻辑类不应该具有自定义SMF。
- 将包含不可复制对象的对插入到映射中
- 通用参考 l 值不复制对象
- 为什么具有 2 个参数参数的构造函数接受复制对象作为 1 个参数参数?
- 为什么从引用创建共享指针会复制对象?
- 当(且仅当)对象具有复制构造函数时,如何复制对象?
- 我的程序运行良好,可以复制对象,但是当我使用复制分配(=)时,它仍然可以正常运行.为什么不给错误
- 如何复制对象
- 如何构造不可移动不可复制对象的元组?
- 不可复制对象数组的 consexpr 初始化?
- 提升::可选与标准::不可复制对象的可选
- 如何在C++中通过引用复制对象
- 从指针复制对象
- 删除浅表的复制对象和源对象
- 使用realloc可以安全地重新分配琐碎的可复制对象的存储吗
- C 返回复制对象
- 如何将智能指针作为类属性来复制对象
- 为什么在复制对象时忽略顶级常量
- std::shared_ptr 在复制对象时导致问题
- 安全复制对象的替代方案
- 当类包含boost::container::flat_set时,复制对象时出错