【问题标题】:Why is std::mutex neither copyable nor movable? [duplicate]为什么 std::mutex 既不可复制也不可移动? [复制]
【发布时间】:2020-10-03 17:41:55
【问题描述】:

有人能说出std::mutex 既不可复制也不可移动的原因吗? 有人告诉我,这与避免资源浪费有一定的关系。为什么std::mutex的拷贝构造函数要标记为已删除?如果没有,是否有任何潜在问题?它的复制构造函数被清楚地标记为已删除,但我没有看到它的移动构造函数的这样声明。那么为什么cppreferencestd::mutex 是不可移动的呢?

【问题讨论】:

  • 从逻辑上讲,复制互斥体意味着什么?这与从头开始创建互斥锁有什么区别?
  • @TonyTannous 对不起,也许我误导了你。我完全理解为什么 std::mutex 不应该是可移动的。但我还有一个问题。 它的复制构造函数被明确标记为已删除但我没有看到它的移动构造函数有这样的声明。那么为什么 cppreference 说 std::mutex 是不可移动的呢?

标签: c++ c++11 constructor mutex copy-constructor


【解决方案1】:

扩展一下其他答案,基本锁(如互斥锁)是语言设计中提供原子操作的最基本对象,此处为lockunlock。它们可能拥有一个操作系统实现的句柄 (native_handle),它是硬件实现对象的句柄,甚至可能跳过中间句柄。

当然,复制这样的句柄并非易事(您不能简单地复制一个硬件,有时甚至是操作系统句柄)。移动它可能会更糟——移动会使对象处于未指定的状态,但从本质上讲,互斥锁是跨线程共享的。如果您在一个线程上执行它,您将不得不以某种方式通知所有其他线程 - 更有可能您只会遇到破坏代码。这是没有潜在好处的大量开销(我可以看到)。

至于为什么没有在您的参考中明确删除移动构造函数 - 如果有(非默认)定义的 destructor (12.8, comment 9),则不会创建默认的 move 构造函数,因此无需删除它。

【讨论】:

  • “甚至可以跳过中间”是什么意思?你能详细解释一下吗?
  • @John 某些操作系统可能允许编译后的程序直接将指令写入实现原子操作的硬件,而不是提供操作系统句柄。除此之外,我认为应该提出一个新问题。
  • 如果我理解你的意思,也许它是由程序集直接实现的,所以没有操作系统句柄。我说的对吗?
  • @John 是的(真的是机器代码),但您正在深入研究深入的实现细节,这些细节对于不自己实现实时机器的人来说通常无关紧要。无论是操作系统提供句柄还是直接访问硬件,关键在于所有权不能轻易复制或以有意义的方式移动。
  • 感谢您的澄清。根据文档(en.cppreference.com/w/cpp/thread/mutex/~mutex),我只能看到std::mutex 的默认destructor,没有声明用户定义的destructor。引用您的答案[强调我的]:“没有创建默认移动构造函数如果有(非默认)定义的析构函数(12.8,评论9),因此无需删除它。 "能否请您更详细地解释一下。
【解决方案2】:

除了下面的答案之外,互斥体句柄也不能在操作系统级别复制。在 Windows 中,您必须创建一个命名互斥体,然后基于该名称打开一个新句柄,例如,打算在新线程/进程中使用,而 std::mutex 永远不会创建命名对象。因此,即使有复制构造函数,也不可能实现它。

一般来说,同步对象的语义与其他一些普通对象的语义不同,即使它们显然包装在 C++ 类中

【讨论】:

  • 但是移动构造函数呢?
  • @Michael Chourdakis 引用:“因此,即使有复制构造函数,也不可能实现它。”为什么?你能更详细地解释一下吗?如果实现了复制 ctor,会不会有任何潜在的问题?
  • Windows 中没有用于复制互斥锁的 API。至于搬家,有可能,但为什么会这样呢?它只会导致错误。
  • @Michael Chourdakis 为什么不提供呢?
【解决方案3】:

std::mutex 没有复制构造函数。如果您发送一个副本,每个人都会锁定它自己的副本,并且您不会阻止竞争条件。帮助程序员不要自取其辱。

30.4.1 Mutex requirements

互斥对象有助于防止数据竞争并允许 执行代理之间的数据安全同步 (30.2.5)。一个 执行代理从它成功调用一个互斥锁开始就拥有一个互斥锁 锁定功能,直到它调用解锁。

30.2.5 可锁定类型的要求 [thread.req.lockable]

执行代理是一个实体,例如可以执行工作的线程 与其他执行代理并行

如果您可以搬家,那么所有权要求就被打破了。显然它已经在why std::mutex doesn't have a move constructor? 之前被问过了

在@Kabanus 上添加和引用草稿,回答为什么移动未标记为已删除

12.8 复制和移动类对象 [class.copy]

20 如果类 X 的定义没有明确声明移动 赋值运算符,一个将被隐式声明为默认如果 只有当

  • []
  • []
  • []
  • X 没有用户声明的析构函数
  • []

【讨论】:

  • 为什么std::mutex的copy ctor应该被标记为已删除?如果没有,有什么潜在的问题吗?
  • @John 可能满足 Lockable 的要求。
  • 那搬家怎么样?
  • @einpoklum A mutex object facilitates protection against data races and allows safe synchronization of data between execution agents (30.2.5). An execution agent owns a mutex from the time it successfully calls one of the lock functions until it calls unlock. 如果你能搬家,那么它与 c++11 工作草案相矛盾。不是吗?
  • 什么是“执行代理”?
猜你喜欢
  • 1970-01-01
  • 2013-05-17
  • 2012-07-30
  • 1970-01-01
  • 2012-04-17
  • 1970-01-01
  • 2014-04-25
  • 2016-07-03
相关资源
最近更新 更多