【问题标题】:Why move constructor is called twice when passing temporaries to thread function? [duplicate]为什么将临时对象传递给线程函数时会调用两次移动构造函数? [复制]
【发布时间】:2018-10-26 00:48:58
【问题描述】:

在下面的代码中,考虑到我的线程函数通过右值引用获取参数,我无法理解为什么类的移动构造函数被调用两次,所以我希望当参数被移动到线程构造函数时,移动构造函数只会被调用一次。有人可以提供有关线程构造函数如何工作以及它如何将参数传递给线程函数的见解。

#include <iostream>
#include <thread>
#include <chrono>
class Test {
  public:
  Test() {}
  Test(Test&&)
  {
    std::cout<<"Move Constructor Called..."<<std::endl;
  }
};
void my_thread_func(Test&& obj)
{
  using namespace std::chrono_literals;
  std::cout<<"Inside thread function..."<<std::endl;
  std::this_thread::sleep_for(2s);
}
int main() {
  std::thread t(my_thread_func,Test());
  std::cout << "Hello World!\n";
  t.join();
  return 0;
}

这个问题不关心线程构造函数参数是按值传递的,而是更关心为什么移动构造函数被调用两次?

【问题讨论】:

  • 您是否逐步调试过?在std::cout&lt;&lt;"Move Constructor Called..."&lt;&lt;std::endl; 处设置一个断点,当执行在那里停止时,查看调用堆栈(从那里调用了Test::Test())。我怀疑一个电话是std::thread::thread(),另一个电话是my_thread_func()。我很乐意阅读您可以在调试器中观察到的内容。
  • @Scheff 尝试在 onlinegdb.com 上逐步调试,但无法理解调用堆栈,因为似乎一个调用来自线程构造函数,另一个来自线程 impl
  • @Kapil 我在想,如果有一个 std::thread 对象按值取 Test,我们还可以看到(至少在 gcc 上)有一个 std::shared_ptr_Impl_BaseTest 如何在不额外移动的情况下从 std::thread 传递到 _Impl_Base
  • 我已经深入研究了 gcc7.3 中 std::thread 的实现。它调用一个函数,该函数创建一个“调用者”结构,该结构本质上包含一个线程函数及其参数的元组,并按值返回它。然后实现在堆上分配另一个“状态”结构,其中包含“调用者”的副本。那是两个构造函数。我认为这是多余的,实现可以直接在堆上创建对象,而无需构造临时调用程序。不过,实现必须变得更复杂一些。
  • 我们也有这个问题phrased in terms of copies

标签: c++ multithreading std stdthread perfect-forwarding


【解决方案1】:

标准允许额外的移动构造,但可能效率较低。错过优化的错误是 https://gcc.gnu.org/PR69724,并且已在即将发布的 GCC 10 版本中修复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-19
    • 2018-01-20
    • 2020-09-06
    • 2021-11-22
    • 1970-01-01
    • 2013-05-20
    • 2013-07-10
    • 2011-06-11
    相关资源
    最近更新 更多