C++ 多线程的错误和如何避免(1)

在终止程序之前没有使用 join() 等待后台线程

前提分析:线程分为 joinable  状态和 detached 状态

添加 .join() 这句代码的时候,就表示主线程需要等待子线程运行结束回收掉子线程的资源后,再往下运行,否则就会产生一种情况:当子线程还没有运行完主线程先运行完了,那么就会结束这个进程,从而中断了子线程的运行。因此 join()函数的作用就是使主线程在此阻塞,等待子线程运行结束并回收其资源,再往下运行。

添加 .detach() 的作用是将主线程与子线程分离,主线程将不再等待子线程的运行,也就是说两个线程同时运行,当主线程结束的时候,进程结束。

在 detach  的时候,这个子线程将脱离主线程的控制,子线程独立分离出去并在后台运行。当主线程结束的时候,进程也就结束,所以子线程的输出不再显示出来,但是不会中断,会在后台继续运行,当子线程运行完以后,资源会被运行时库进行回收。

问题:如果我们在主程序终止之前忘记 join 或 detach,则会导致程序崩溃。

比如:

#include <iostream>
#include <thread>

using namespace std;

void LaunchRocket() { cout << "Launching Rocket" << endl; }

int main() {
  thread t1(LaunchRocket);
  // t1.join(); // somehow we forgot to join this to main thread - will cause a
  // crash.
  return 0;
}

运行后,会报错。

C++ 多线程的错误和如何避免(1)

 

解析:

这是因为在 main 函数结束时,线程 t1 超出范围后会自动析构,在析构过程中,会有一个检查来观察线程 t1 是否是 joinable 状态,如果是 joinable 状态,则调用 std::terminate

~thread() _NOEXCEPT
{  // clean up
  if (joinable())
    _XSTD terminate();
}

而我们创建的 t1 没有添加 .join(),还处于 joinable 状态,此时析构时会调用 terminate(),而 terminate() 会默认调用 abort(),从而报错

补充:

1. joinable() 代表该线程是可执行线程,用于检测线程是否有效。

它会返回一个布尔值来表示当前的线程是否是可执行线程(能被 join 或者 detach),因为相同的线程不能 join 两次,也不能 join  完再  detach,同理也不能 detach

通常以下几种情况会导致线程成为 not-joinable

       1)由 thread 的缺省构造函数而造成的(thread() 没有参数)。

       2)该 thread 被 move 过(包括 move 构造和 move 赋值)。

       3)该线程被 join 或者 detach 过。

比如:

...

if (t1.joinable()) {
  cout << "joinable() == true" << endl; // 没有添加 .join(),joinable() == true
} else {
  cout << "joinable() == fasle" << endl; // 添加 .join(),joinable() == true
}

...

2. 摘自 https://en.cppreference.com/w/cpp/thread/thread/~thread

四种可以安全析构的情况是:

  1. 默认构造函数创建的 std::thread,在这种情况下,没有实际的线程被创建。
  2. 被移动过的线程,在这种情况下,移动的对象关联了线程而被移动的对象无关联线程。
  3. 调用了 join(),在这种情况下,join() 函数会堵塞直到被关联的线程执行结束。
  4. 调用了detach(),在这种情况下,被关联的线程会被解除关联。

参考:

1. C++ 线程问题,为什么会崩溃?

2. C++ 多线程中的join, detach, joinable

3. Top 20 C++ multithreading mistakes and how to avoid them

原文链接: https://www.cnblogs.com/strive-sun/p/16267463.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++ 多线程的错误和如何避免(1)

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/404540

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月25日 下午4:37
下一篇 2023年4月25日 下午4:37

相关推荐