【问题标题】:std::thread, class constructor and destructorstd::thread,类构造函数和析构函数
【发布时间】:2012-10-19 22:57:13
【问题描述】:

在 C++11 中测试线程时,我创建了以下示例:

#include <iostream>
#include <thread>

class Foo {
public:
    Foo(void) {
        std::cout << "Constructor called: " << this << std::endl;
    }
    ~Foo(void) {
        std::cout << "Destructor called: " << this << std::endl;
    }
    void operator()() const {
        std::cout << "Operatior called: " << this << std::endl;
    }
};

void test_normal(void) {
    std::cout << "====> Standard example:" << std::endl;
    Foo f;
}

void test_thread(void) {
    std::cout << "====> Thread example:" << std::endl;
    Foo f;
    std::thread t(f);
    t.detach();
}


int main(int argc, char **argv) 
{
    test_normal();
    test_thread();

    for(;;);
}

打印以下内容:

为什么要为线程调用 6 次析构函数?为什么线程会报告不同的内存位置?

编辑 添加移动和复制构造函数输出时:

【问题讨论】:

    标签: c++ multithreading c++11 stdthread


    【解决方案1】:

    函数对象将被移动或复制。您没有在输出中考虑任何这些。

    【讨论】:

      【解决方案2】:

      添加一个复制构造函数并将构造函数移动到您的类。

      Foo(Foo const&) { std::cout << "Copy Constructor called: " << this << std::endl; }
      Foo(Foo&&) { std::cout << "Move Constructor called: " << this << std::endl; }
      

      现在,如果您运行 code,输出(在 gcc 4.7.2 上)如下所示:

      ====> Standard example:
      Constructor called: 0xbff696ff
      Destructor called: 0xbff696ff
      ====> Thread example:
      Constructor called: 0xbff696ff
      Copy Constructor called: 0xbff696cf
      Move Constructor called: 0x93a8dfc
      Destructor called: 0xbff696cf
      Destructor called: 0xbff696ff
      Operator called: 0x93a8dfc
      Destructor called: 0x93a8dfc
      

      如您所见,对析构函数的调用次数与对各种构造函数的调用次数相匹配。

      我怀疑 gcc 设法省略了一些 MSVC 似乎正在进行的复制/移动构造调用,因此对析构函数的调用比您的示例要少。


      此外,您可以通过std::moveFoo 对象传递给线程构造函数来完全避免复制构造。

      test_thread中将线程构造线改为

      std::thread t(std::move(f));
      

      现在输出如下所示:

      ====> Standard example:
      Constructor called: 0xbfc23e2f
      Destructor called: 0xbfc23e2f
      ====> Thread example:
      Constructor called: 0xbfc23e2f
      Move Constructor called: 0xbfc23dff
      Move Constructor called: 0x9185dfc
      Destructor called: 0xbfc23dff
      Destructor called: 0xbfc23e2f
      Operator called: 0x9185dfc
      Destructor called: 0x9185dfc
      

      【讨论】:

      • 我明白了。我现在将我的输出添加到帖子中。看起来我有 3 个额外的移动构造函数调用。这和编译器有关吗?
      • 当你创建Foo对象时会有一个构造调用,当你将它传递给线程构造函数时会有一个复制构造调用。其余的是与实现相关的。
      【解决方案3】:

      因为您的 Foo 在堆栈上,而不是在堆上。这意味着您在 test_thread 中分配一个新的,然后在您调用 std::thread(f) 并再次在 thread(f) 中复制它。

      您需要创建一个指针以在堆上分配,并传递它以便每次都不会复制对象,使用堆(新)分配它。

      【讨论】:

      • 我不确定我是否理解。我以为它只会被复制一次?
      • 将值传递给线程构造函数时复制,线程构造函数将其复制到基中。这意味着您将首先删除 test_thread 和 test_normal 中的副本,然后是线程构造函数中的副本,然后是线程本身中的副本。
      • 我了解线程测试中的两个销毁调用。但是还有4个??
      • 您的类没有正确的复制或移动构造函数。您会看到额外的,因为有些来自普通的 foo,实际上您首先有一个构造函数,然后是一个副本,然后是一个移动。那是 3。基本输出至少还有 1 个。
      【解决方案4】:

      编译器会添加默认的移动和复制构造函数,如果你自己不做,检查一下

      https://ideone.com/wvctrl

      #include <iostream>
      #include <thread>
      
      class Foo {
      public:
          Foo(Foo&& f) {
              std::cout << "Constructor Foo&& called: " << this << std::endl;
          }
          Foo(const Foo& f) {
              std::cout << "Constructor const Foo& called: " << this << std::endl;
          }
          Foo(void) {
              std::cout << "Constructor called: " << this << std::endl;
          }
          ~Foo(void) {
              std::cout << "Destructor called: " << this << std::endl;
          }
          void operator()() const {
              std::cout << "Operatior called: " << this << std::endl;
          }
      };
      
      void test_normal(void) {
          std::cout << "====> Standard example:" << std::endl;
          Foo f;
      }
      
      void test_thread(void) {
          std::cout << "====> Thread example:" << std::endl;
          Foo f;
          std::thread t(f);
          t.detach();
      }
      
      
      int main(int argc, char **argv) 
      {
          test_normal();
          test_thread();
      
          for(;;);
      }
      

      它表明所有 ctors 都与 dtors 配对。

      同时查看这个 SO:

      Rule-of-Three becomes Rule-of-Five with C++11?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-13
        • 2013-07-07
        • 2014-11-12
        • 1970-01-01
        • 2014-05-27
        相关资源
        最近更新 更多