std:mutex'es 作为将进入容器的对象的成员?
std:mutex'es as members of objects that will go into containers?
我知道这不是一个问题,而是一个讨论,但我相信可以提供一个或多个答案,所以开始吧。
我有一个像这样的班级
class MyAwesomeObject {
public:
std::mutex theListMutex;
std::list<int> theList;
};
现在,我相信这个意图是显而易见的,而且这个例子是故意的学术性的——尽管离我的现实不远——所以让我们继续吧。然后,我的应用程序处理大量这样的对象,所有这些对象都存储在其他地方的向量中,一切都很好。当我编译的时候麻烦就开始了。我使用的是VS2012,但我相信其他编译器可能会发出类似的错误:
error C2248: 'std::mutex::mutex' : cannot access private member declared in class 'std::mutex'
1> c:program filesmicrosoft visual studio 11.0vcincludemutex(116) : see declaration of 'std::mutex::mutex'
1> c:program filesmicrosoft visual studio 11.0vcincludemutex(107) : see declaration of 'std::mutex'
1> This diagnostic occurred in the compiler generated function 'MyApp::MyAwesomeObject ::MyAwesomeObject (const MyApp::MyAwesomeObject &)'
这个意思我很清楚,在SO中已经讨论了很多其他问题,所有这些问题基本上都说"互斥体不能复制",我对此很满意。所以到目前为止,我的策略是将互斥体设为shared_ptr<mutex>
并使用它。唯一的"缺点"是,现在我访问互斥体时必须使用愚蠢的去引用语法,这当然一点也不傻,只是有点尴尬,站在我到处使用的所有其他圆点符号之间。
现在,我的问题是:使用共享指针是解决问题的正确方法吗?我可能使用了一个唯一的指针,因为据我所知,我没有将所有权转移给其他任何人(除非对作为对象成员的指针调用方法实际上是所有权转移的一种形式)?有没有其他方法可以绕过互斥对象不能复制的事实?
所以到目前为止,我的策略是将互斥对象设置为
shared_ptr<mutex>
等等。为什么互斥体会在不同的实例之间共享?你意识到这最终会用同一个互斥体保护多个theList
吗?
你不能只是把shared_ptr
放在事情上,然后假装它解决了问题。在这种情况下,它会更改语义。我希望这里的语义是正确的:每个新的MyAwesomeObject
对象都有自己的互斥对象来保护自己的theList
。
否,使用std::shared_ptr
不是处理此问题的正确方法。
如果希望对象是可复制的,那么定义一个复制构造函数,将互斥锁锁定在源中,然后复制内容。Mike Spertus为我的博客写了一篇关于这一点的客座文章:http://www.justsoftwaresolutions.co.uk/threading/thread-safe-copy-constructors.html
如果您只希望对象是可移动的(正如Jonathan所指出的,这是将其存储在向量中所需的全部内容),那么您可以如上所述定义一个移动构造函数,或者按照Jonathan的建议使用std::unique_ptr<std::mutex>
。
不需要shared_ptr
,因为互斥对象的所有者永远不会超过一个。
我会使用std::unique_ptr<std::mutex>
,它使对象只能移动,不可复制,但这可以将它们存储在vector
中。通过这种方式,您可以将列表和相关互斥对象的所有权从一个对象转移到另一个对象,但不能复制它们。
这意味着MyAwesomeObject
将有一个空的"从"状态,在该状态下它没有互斥,因此不应该使用它的列表。你应该添加一个成员来查询它是否处于该状态,这样用户就可以判断他们是否得到了一个没有互斥的空对象:
class MyAwesomeObject {
std::unique_ptr<std::mutex> theListMutex;
std::list<int> theList;
public:
bool valid() const { return (bool)theListMutex; }
};
Mutex策略应该基于"事务范围"概念,而不是每个objet/或每个this。根据这一点。
现在交易范围可以是
- A: 操作单个共享数据结构,如列表等
- B: 操作多个共享数据结构,例如多个列表、计数器和;等等
- C: 操作多个共享数据结构&共享extrenal[可以是文件描述符或数据库连接等]资源
另一种可能性是使用deque
或list
而不是vector
,因为它不会对存储的类型强加可移动/可复制的要求(当然,假设您使用emplace_*
方法构造将元素存储在容器中,并且您没有复制容器)。
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 在运行时有条件地删除类成员或跳过调用该成员对象的构造函数
- C++是否有定义的方法来传递指向类的成员对象的成员函数的指针
- (2 问题)"类"类型重新定义(即使 #pragma 一次),以及静态函数内的静态成员对象初始化?
- 声明成员对象而不调用其默认构造函数
- 指向成员对象的指针 - 中断线程
- 是否有更好的方法来封装成员对象可以访问的共享存储池?
- 应该在成员对象上调用析构函数
- 调用成员对象的构造函数
- 将 const 类型引用对象注册为类成员对象C++
- 修改类 c++ 中的成员对象
- 从成员对象调用方法
- 从对象调用成员对象,错误:引用非常量值的初始值必须是左值
- 构造函数,成员对象
- 不能使外部类成为内部类内的成员对象
- 使用从另一个类继承的类的对象初始化成员对象
- 如何避免需要在初始化列表中初始化成员对象
- 初始化具有参数的类成员对象的正确方法
- 将指针添加到成员对象中的指针动态阵列
- C 将成员对象函数分配给类成员功能