【问题标题】:Move constructor for std::mutex移动 std::mutex 的构造函数
【发布时间】:2011-11-25 07:27:01
【问题描述】:

c++ 标准库中的许多类现在都有移动构造函数,例如 -

thread::thread(thread&& t)

但似乎 std::mutex 没有。我知道它们不能被复制,但是例如能够从“make_mutex”函数返回一个似乎是有意义的。 (不是说有用,只是觉得有意义)

std::mutex 没有移动构造函数有什么原因吗?

【问题讨论】:

  • 我在那里遇到了同样的问题,因为在我的代码中,我需要阻止对一种方法的访问,直到另一种方法完成。可悲的是,运行方法的对象最好在工厂内创建,由于同样的限制,我不能简单地将创建的“仿函数”移出工厂方法。这个帖子真的鼓励我尝试使用std::future 作为分离模型。
  • make_mutex 函数的想法仍然有用,抛开所有其他讨论。使用 C++17 并保证复制省略,应该可以这样做。互斥体不会被移动 - 它将在调用者的位置就地构造,但被调用者(例如:make_mutex)可以决定如何进行构造。

标签: c++ c++11


【解决方案1】:

嗯...主要是因为我认为他们不应该移动。字面上地。

在某些操作系统中,互斥锁可能被建模为句柄(因此您可以复制它们),但 IIRC 的 pthreads 互斥锁是就地操作的。如果你要重新定位它,任何线程安全都会飞出窗口(其他线程怎么知道互斥锁刚刚更改了它的内存地址......)?

【讨论】:

  • 好吧,我想这是有道理的 :) 我在想代表互斥锁的互斥对象,所以你可以将它移动到另一个对象,但如果你认为它是 being互斥体那么移动它没有意义,你是对的,
  • @JohnB:你可以用unique_lock<mutex>做你想做的事。这是一个可锁定的对象(具有互斥体的API),并且可以移动。哦,但我不会一次将它暴露给多个线程。但是您可以在每个线程中都有一个引用相同的mutex
  • 这很好地解释了我的问题。这意味着您不能(也不应该)将互斥锁直接放入容器中。
  • @T.E.D.除非您可以保证存储永远不会移动(std::array<std::mutex, 100> 是一个容器。boost::intrusive::slist<std::mutex, ...> 也是如此 :))。但除此之外,是的。您可以随时hack it with a shared_ptr<std::mutex> 进行各种操作。
  • @sehe - 我就这个话题提出了一个问题:stackoverflow.com/questions/27276555/…
【解决方案2】:

请记住,C++ 将“不为不使用的东西付费”的理念铭记于心。例如,让我们考虑一个使用不透明类型表示互斥体的假想平台;我们称该类型为mutex_t。如果对该互斥体进行操作的接口使用mutex_t* 作为参数,例如void mutex_init(mutex_t* mutex); 来“构造”互斥体,则互斥体的地址很可能是用于唯一标识互斥体的地址。如果是这种情况,则意味着mutex_t 不可复制:

mutex_t kaboom()
{
    mutex_t mutex;
    mutex_init(&mutex);
    return mutex; // disaster
}

这里不能保证mutex_t mutex = kaboom();&mutex 与功能块中的&mutex 的值相同。

当实现者想要为该平台编写std::mutex 时,如果要求该类型是可移动的,那么这意味着内部mutex_t 必须放在动态分配的内存中,所有的相关处罚。

另一方面,虽然现在std::mutex不可可移动的,但很容易从函数中“返回”一个:改为返回一个std::unique_ptr<std::mutex>。这仍然会支付动态分配的成本但仅在一个地方。不需要移动std::mutex 的所有其他代码都不必为此付费。

换句话说,由于移动互斥锁不是互斥锁的核心操作,因此不需要std::mutex 可移动并不会删除任何功能(感谢不可移动 em> T => movable std::unique_ptr<T> 转换),并且会比直接使用原生类型产生最小的开销。


std::thread 可以类似地指定为不可移动,这会使典型的生命周期如下:运行(与执行线程相关联),在调用有值的构造函数之后;并在调用joindetach 后分离/加入(与无执行线程相关联)。据我了解,std::vector<std::thread> 仍然可以使用,因为类型本来是EmplaceConstructible

编辑:不正确!该类型仍然需要是可移动的(毕竟重新分配时)。所以对我来说,这就是足够的理由:通常将std::thread 放入容器中,如std::vectorstd::deque,因此该类型的功能是受欢迎的。

【讨论】:

  • 移动 T 很少是“关于 T 的核心操作”。
猜你喜欢
  • 2015-03-16
  • 1970-01-01
  • 2014-05-02
  • 2018-03-12
  • 2015-04-26
  • 2013-08-24
  • 2015-02-14
  • 1970-01-01
相关资源
最近更新 更多