std::mutex 作为类成员,并将类对象存储到容器中

Posted

技术标签:

【中文标题】std::mutex 作为类成员,并将类对象存储到容器中【英文标题】:std::mutex as class member, and store class obect to container 【发布时间】:2019-03-05 12:51:33 【问题描述】:

以下是重现错误的最少代码。

#include <iostream>
#include <mutex>
#include <vector>

class A 
    std::mutex mutex;
    public:
    A();
;
int main() 

    std::vector<std::pair<std::string,A>> aa;
    A a;
    //aa.push_back(std::make_pair(std::string("aa"),A()));
    //aa.push_back(std::make_pair(std::string("aa"),a));
    aa.push_back(std::make_pair(std::string("aa"),std::move(a)));    

以下是错误。

Microsoft (R) C/C++ 优化编译器版本 19.16.27026.1 for x64 版权所有 (C) 微软公司。保留所有权利。

>   C:\Program Files (x86)\Microsoft Visual
> Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\xlocale(319):
> warning C4530: C++ exception handler used, but unwind semantics are
> not enabled. Specify /EHsc    C:\Program Files (x86)\Microsoft Visual
> Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\utility(405):
> error C2440: '<function-style-cast>': cannot convert from 'initializer
> list' to '_Mypair'    C:\Program Files (x86)\Microsoft Visual
> Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\utility(405):
> note: No constructor could take the source type, or constructor
> overload resolution was ambiguous
>   ..\examples\json_object\json.cpp(16): note: see reference to function
> template instantiation 'std::pair<std::string,A>
> std::make_pair<std::string,A>(_Ty1 &&,_Ty2 &&)' being compiled            with
>           [
>               _Ty1=std::string,
>               _Ty2=A          ]

gcc 编译器的类似错误。 当我从类中删除 std::mutex 或不将对象推送到 std::vector 时,它可以正常编译。

【问题讨论】:

【参考方案1】:

根据std::mutex 上的文档。

std::mutex 既不能复制也不能移动

由于类A 包含一个std::mutex 变量mutex,它也不能移动。

【讨论】:

解决这个问题的方法是使用(智能)指向 std::mutex 的指针。【参考方案2】:

作为 P.W.指出,std::mutex 既不可复制也不可移动,这是有充分理由的。拥有互斥锁的全部意义在于防止同时多线程访问某些数据。移动操作本身需要保护,并且互斥体应该被移动操作使用

以下示例为类提供了一些可移动数据,并展示了如何在移动操作中使用互斥锁(复制操作类似):

#include <iostream>
#include <mutex>
#include <vector>
#include <memory>

class A 
public:
  A() ;

  // Move constructor
  A(A&& other) 
    std::lock_guard<std::mutex> guard(other.m_mutex);
    m_data = std::move(other.m_data);
  

  // Move operator
  A& operator=(A&& other) 
    if (this == &other) return *this;

    // Lock this and other in a consistent order to prevent deadlock
    std::mutex* first;
    std::mutex* second;
    if (this < &other) 
      first = &this->m_mutex;
      second = &other.m_mutex;
     else 
      first = &other.m_mutex;
      second = &this->m_mutex;
    
    std::lock_guard<std::mutex> guard1(*first);
    std::lock_guard<std::mutex> guard2(*second);

    // Now both this and other are safe to access.  Do the actual data move.
    m_data = std::move(other.m_data);
    return *this;
  

private:
  std::mutex m_mutex;
  std::unique_ptr<int> m_data;
;

int main() 
  std::vector<std::pair<std::string,A>> aa;
  A a1;
  A a2;
  a1 = std::move(a2);
  aa.emplace_back("aa", std::move(a1));

【讨论】:

【参考方案3】:

正如 P.W 所指出的和怪胎提供的提示,我想出了以下解决方案。

#include <iostream>
#include <mutex>
#include <vector>
#include <memory>

class A 
    std::mutex mutex;
    public:
    A();
;
int main() 

    std::vector<std::pair<std::string,std::shared_ptr<A>>> aa;
    A a;
    //aa.push_back(std::make_pair(std::string("aa"),A()));
    //aa.push_back(std::make_pair(std::string("aa"),a));
    aa.push_back(std::make_pair(std::string("aa"),std::make_shared<A>()));   

我修改了我的容器来存储对象的智能指针而不是对象本身。

【讨论】:

FWIW,你真的应该使用std::unique_ptr,除非你真的需要共享所有权。 90% 的时候,你不会。

以上是关于std::mutex 作为类成员,并将类对象存储到容器中的主要内容,如果未能解决你的问题,请参考以下文章

当我在 C++ 中派生一个类时,它是不是会创建一个基类对象并将其作为我的成员变量存储在派生类中?

使用移动语义在构造函数中初始化类成员

C++-mutex(待验证)

在 C++ 线程中使用类成员

如何在类上下文中使用std :: mutex

确保当前线程持有 C++11 互斥锁