【问题标题】:Start thread within member function using std::thread & std::bind使用 std::thread 和 std::bind 在成员函数中启动线程
【发布时间】:2019-10-07 10:17:19
【问题描述】:

我对以下代码快照的查询很少。

1) 关于 pthread_create(),假设 Thread_1 创建 Thread_2。据我了解,Thread_1 可以在不加入的情况下退出,但 Thread_2 仍将继续运行。在没有 join() 的下面示例中,我无法运行线程并且我看到异常。

2) 在几个示例中,我看到没有线程对象的线程创建如下。但是当我这样做时,代码就会终止。

std::thread(&Task::executeThread, this);

I am compiling with below command.
g++ filename.cpp -std=c++11 -lpthread

但它仍然异常终止。这是创建线程的正确方法还是有任何不同版本的 C++(在我的项目中他们已经在编译但不确定版本)。

3) 在我的项目代码的几个示例中,我看到了以下创建线程的方式。但我无法执行以下示例。

std::thread( std::bind(&Task::executeThread, this) );

下面是我的代码快照。

#include <iostream>
#include <thread>

class Task
{
    public:
    void executeThread(void)
    {
        for(int i = 0; i < 5; i++)
        {
           std::cout << " :: " << i << std::endl;
        }
    }

    void startThread(void);
};

void Task::startThread(void)
{
    std::cout << "\nthis: " << this << std::endl;

#if 1
    std::thread th(&Task::executeThread, this);
    th.join(); // Without this join() or while(1) loop, thread will terminate
    //while(1);
#elif 0
    std::thread(&Task::executeThread, this);  // Thread creation without thread object
#else
    std::thread( std::bind(&Task::executeThread, this) );
    while(1);
#endif
}

int main()
{
    Task* taskPtr = new Task();
    std::cout << "\ntaskPtr: " << taskPtr << std::endl;

    taskPtr->startThread();

    delete taskPtr;
    return 0;
}

感谢和问候

毗湿奴比玛

【问题讨论】:

  • 也许你误解了这些例子。你能展示一个完整的例子来说明std::thread( std::bind(&amp;Task::executeThread, this) ); 的一种用法吗?目前您对我们看不到的其他代码的引用有点令人困惑
  • 你应该几乎总是加入线程。一个例外是当您愿意在其他线程仍在运行时退出程序,从而有效地杀死它们。
  • 在您的情况下,“Thread_1”是主线程。
    所有线程都属于/由进程管理。如果主线程退出,则进程终止。所以,这就是你无法运行并看到异常的原因。

标签: c++ multithreading c++11


【解决方案1】:

std::thread(&amp;Task::executeThread, this); 语句创建和销毁线程对象。 destructor of std::thread 在线程未加入或分离时调用 std::terminate(如您的语句中所示)。

没有充分的理由在 C++11 中使用 std::bind,因为 lambda 在空间和速度方面更好。

在构建多线程代码时,您需要在编译和链接时指定-pthread 选项。链接器选项-lpthread 既不充分又不必要。

【讨论】:

  • std::bind 的用例在 c++14 之前无法使用 lambda。不过,我认为这个问题没有必要。
  • @eerorika 我只能想到std::bind 默默地忽略了额外的参数,但想了解更多。
  • 移动捕获是我想到的。
  • @eerorika 哦,是的,你是对的,我现在认为 C++14 是理所当然的。
【解决方案2】:

按照设计,您需要加入您生成的所有线程,或将它们分离。参见例如SO question on join/detach

另见cppreference, detach

还要注意重要的警告if main() exits while detached threads are still running

我也 100% 同意另一个答案中关于更喜欢绑定 lambda 的评论。

最后,不要陷入在 C++ 线程上执行 pthread_cancel 的诱惑。参见例如pthread_cancel considered harmful

【讨论】:

    【解决方案3】:
    1. 在 C++ 中,对象具有生命周期。这与在 C 中处理句柄有点不同。在 C++ 中,如果您在一个范围内的堆栈上创建一个对象,那么如果您退出该范围,它将被销毁。这些规则有一些例外,例如 std::move,但根据经验,您拥有对象的生命周期。

    2. 这与上面的答案相同。当您调用std::thread(&amp;Task::executeThread, this); 时,您实际上是在调用线程构造函数。这是线程生命周期和对象生命周期的开始。请注意,您在堆栈上创建了此对象。如果您离开范围{ .. yourcode .. },将调用 DTor。由于您在std::movejoindetatch 之前已完成此操作,因此会调用std::terminate(),这会引发异常。

    3. 您可以通过这种方式创建线程。如果您查看 std::thread::thread (构造函数)的链接文档,则有一个以相同方式创建对象 foo 的示例。你收到什么错误?

    相关文档:

    一个。 std::thread::~thread()

    b. std::thread::thread

    c。 Lifetime in C++

    我个人建议了解 C++ 中对象的生命周期。简而言之,所有对象的生命周期都是在调用其构造函数时开始的。当它们被杀死(如超出范围)时,它们的析构函数被调用。编译器会为你处理这个问题,所以如果你来自 C 语言,它是一个新概念。

    【讨论】:

      【解决方案4】:

      感谢大家的投入。作为线程创建的一部分,我错过了线程对象。因为这个虽然编译,我得到了例外。下面是我更新的代码。所有三种情况都运行良好。

      #include <iostream>
      #include <thread>
      
      class Task
      {
          public:
          void executeThread(std::string command)
          {
              for(int i = 0; i < 5; i++)
              {
                  std::cout << command << " :: " << i << std::endl;
              }
          }
      
          void startThread(void);
          std::thread th2;
          std::thread th3;
      };
      
      void Task::startThread(void)
      {
          std::cout << "\nthis: " << this << std::endl;
      
          #if 0
              std::thread th1(&Task::executeThread, this, "Thread1");
              th1.join(); // Without join(), thread will terminate
          #elif 0
              th2 = std::thread(&Task::executeThread, this, "Thread2");
              th2.join();
          #else
              th3 = std::thread( std::bind(&Task::executeThread, this, "Thread3") );
              th3.join();
          #endif
      }
      
      int main()
      {
          Task* taskPtr = new Task();
          std::cout << "\ntaskPtr: " << taskPtr << std::endl;
      
          taskPtr->startThread();
      
          delete taskPtr;
          return 0;
      }
      

      【讨论】:

      • 这不会解决您的问题。您在这里介绍的只是内存泄漏。你现在没有看到你的问题,因为 taskPtr 是一个引用类型,它不会在这个范围的末尾被破坏。
      • @Freddy:不会删除 taskPtr 或退出 main() 调用 Task() 析构函数。
      猜你喜欢
      • 1970-01-01
      • 2015-06-18
      • 1970-01-01
      • 1970-01-01
      • 2014-01-09
      • 1970-01-01
      • 1970-01-01
      • 2021-04-27
      • 1970-01-01
      相关资源
      最近更新 更多