为什么 std::mutex 既不可复制也不可移动?
Why is std::mutex neither copyable nor movable?
有人能说说为什么std::mutex
既不可复制也不可移动吗?
有人告诉我,这与避免资源浪费有一定关系。为什么 std::mutex
的复制构造函数应该被标记为已删除?如果没有,是否存在任何潜在问题?它的复制构造函数被明确标记为已删除,但我没有看到它的移动构造函数有这样的声明。那为什么cppreference说std::mutex
不可移动呢?
std::mutex 没有复制构造函数。如果您发送一份副本,每个人都会锁定自己的副本,并且您不会阻止竞争条件。帮助程序员不要搬起石头砸自己的脚。
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.
30.2.5 可锁定类型的要求 [thread.req.lockable]
An execution agent is an entity such as a thread that may perform work
in parallel with other execution agents
如果你可以移动,那么所有权要求就被打破了。显然之前已经有人问过 why std::mutex doesn't have a move constructor?
在@Kabanus 上添加和引用草稿,回答为什么移动没有被标记为删除
12.8 复制和移动 class 个对象 [class.copy]
20 If the definition of a class X does not explicitly declare a move
assignment operator, one will be implicitly declared as defaulted if
and only if
- []
- []
- []
- X 没有用户声明的析构函数
- []
除了下面的答案之外,互斥句柄也不能在 OS 级别复制。在 Windows 中,您必须创建一个命名互斥体,然后根据该名称打开一个新句柄,例如,打算在新的 thread/process 中使用,而 std::mutex 从不创建命名对象.所以即使有拷贝构造函数,也不可能实现。
通常,同步对象的语义与其他一些普通对象的语义不同,即使它们显然被包装在 C++ 中 class
扩展一下其他答案,互斥锁等基本锁是提供原子操作的语言设计中最基本的对象,这里是lock
和unlock
。这些可能拥有一个 OS 实现的句柄 (native_handle
),它是硬件实现对象的句柄,甚至可能跳过中间句柄。
复制这样的句柄当然不是一件容易的事(你不能复制一个硬件,有时甚至是 OS 句柄,这很简单)。移动它可能更糟 - 移动会使对象处于未指定状态,但就其本质而言,互斥锁是跨线程共享的。如果你在一个线程上处理它,你将不得不以某种方式通知所有其他线程——更有可能你只会破坏代码。这是没有潜在好处的大量开销(我可以看到)。
关于为什么未在您的参考中明确删除移动构造函数 - 如果定义了(非默认)destructor
(12.8, comment 9,则不会创建默认 move
构造函数), 所以没有必要删除它。
有人能说说为什么std::mutex
既不可复制也不可移动吗?
有人告诉我,这与避免资源浪费有一定关系。为什么 std::mutex
的复制构造函数应该被标记为已删除?如果没有,是否存在任何潜在问题?它的复制构造函数被明确标记为已删除,但我没有看到它的移动构造函数有这样的声明。那为什么cppreference说std::mutex
不可移动呢?
std::mutex 没有复制构造函数。如果您发送一份副本,每个人都会锁定自己的副本,并且您不会阻止竞争条件。帮助程序员不要搬起石头砸自己的脚。
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.
30.2.5 可锁定类型的要求 [thread.req.lockable]
An execution agent is an entity such as a thread that may perform work in parallel with other execution agents
如果你可以移动,那么所有权要求就被打破了。显然之前已经有人问过 why std::mutex doesn't have a move constructor?
在@Kabanus 上添加和引用草稿,回答为什么移动没有被标记为删除
12.8 复制和移动 class 个对象 [class.copy]
20 If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if
- []
- []
- []
- X 没有用户声明的析构函数
- []
除了下面的答案之外,互斥句柄也不能在 OS 级别复制。在 Windows 中,您必须创建一个命名互斥体,然后根据该名称打开一个新句柄,例如,打算在新的 thread/process 中使用,而 std::mutex 从不创建命名对象.所以即使有拷贝构造函数,也不可能实现。
通常,同步对象的语义与其他一些普通对象的语义不同,即使它们显然被包装在 C++ 中 class
扩展一下其他答案,互斥锁等基本锁是提供原子操作的语言设计中最基本的对象,这里是lock
和unlock
。这些可能拥有一个 OS 实现的句柄 (native_handle
),它是硬件实现对象的句柄,甚至可能跳过中间句柄。
复制这样的句柄当然不是一件容易的事(你不能复制一个硬件,有时甚至是 OS 句柄,这很简单)。移动它可能更糟 - 移动会使对象处于未指定状态,但就其本质而言,互斥锁是跨线程共享的。如果你在一个线程上处理它,你将不得不以某种方式通知所有其他线程——更有可能你只会破坏代码。这是没有潜在好处的大量开销(我可以看到)。
关于为什么未在您的参考中明确删除移动构造函数 - 如果定义了(非默认)destructor
(12.8, comment 9,则不会创建默认 move
构造函数), 所以没有必要删除它。