std:mutex'es 作为将进入容器的对象的成员?

std:mutex'es as members of objects that will go into containers?

本文关键字:成员 对象 mutex es std      更新时间:2023-10-16

我知道这不是一个问题,而是一个讨论,但我相信可以提供一个或多个答案,所以开始吧。

我有一个像这样的班级

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[可以是文件描述符或数据库连接等]资源

另一种可能性是使用dequelist而不是vector,因为它不会对存储的类型强加可移动/可复制的要求(当然,假设您使用emplace_*方法构造将元素存储在容器中,并且您没有复制容器)。