C++:不调用main函数中的join()函数是否可以执行线程?

C++ : Can a thread be executed without calling the join() function in the main function?

请帮我解决一个关于以下代码输出的简单问题。

我以为只有调用join()或detach()函数时才会执行线程

因此,我希望输出仅打印“暂停 2 秒结束,ID = 59306”,而不打印“暂停 1 秒” ended , ID = 10218”因为我以为只有thread 2会被执行,而thread 1NOT 被执行。但是,我错了。

实际上,输出实际上打印了上面提到的两行,这意味着线程 1 和 2 都被执行了。是真的吗?

你能给我解释一下代码实际上是如何执行两个线程的吗?

============================

#include <iostream>       // std::cout
#include <thread>         // std::thread, std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds
 
void pause_thread(int n) 
{
  std::this_thread::sleep_for (std::chrono::seconds(n));
  std::cout << "pause of " << n << " seconds ended , ID = " << std::this_thread::get_id()  << std::endl;
}
 
int main() 
{
  std::cout << "Spawning 2 threads...\n";
  std::thread t1 (pause_thread,1);
  std::thread t2 (pause_thread,2);


  std::cout << "Done spawning threads. Now waiting for them to join:\n";
  //t1.join();
  t2.join();

  //t1.detach();
  //t2.detach();

  std::cout << "All threads joined!\n";

  return 0;
}

实际输出:

Spawning 2 threads...
Done spawning threads. Now waiting for them to join:
pause of 1 seconds ended , ID = 10218
pause of 2 seconds ended , ID = 59306
All threads joined!

===================

更新:

感谢大家的评论和回答。

现在,我理解了我不正确的逻辑,因为我错误地认为只有在调用 join() 或 detach() 函数时才执行线程。

我意识到线程一创建就执行,不需要调用 join() 或 detach()。

加入(): 在 main() 函数中,当我们在线程上调用 join() 后,main() 函数将被阻塞,直到线程完成。线程完成后,main() 函数将恢复,线程的输出将与 main() 函数的任何剩余输出一起显示。

分离(): 在main()函数中,如果我们在一个线程上调用detach(),那么,main()函数和线程都可以运行并发,并且它们之间不会被阻塞,也不会相互依赖以任何方式。在这种情况下,如果 main() 函数在分离线程完成之前完成,那么我们只能看到 main() 函数的输出,而看不到分离线程的输出。但是,如果分离线程在 main() 函数完成之前完成,那么我们可以看到线程和 main() 函数的输出。

线程不是在对它们调用 .join() 时开始执行,而是在它们被构造时开始执行。

.join() 用于阻止调用线程的执行,直到被加入的线程完成执行。由于您观察到的原因,通常需要在 main() 退出之前加入所有线程:

main()到达return语句时,剩下std::thread个对象的范围。如果此时管理线程的 std::thread 上既没有调用 .join() 也没有调用 .detach(),则 std::thread 的析构函数将调用 std::terminate(),其中(默认情况下)中止整个程序的执行。

即使您分离线程而不是加入线程,当 main() 没有通过调用 .join() 阻塞足够长的时间并在线程完成输出之前退出时,也会出现问题.退出main()后,静态存储时长对象被销毁。一个这样的对象是 std::cout。当线程在等待后尝试通过 std::cout 输出时,它已经被破坏并且程序具有未定义的行为。

另请注意,分离 t1 和加入 t2 也不安全,尽管等待时间似乎表明了这一点。确切地安排线程执行的时间取决于操作系统。线程 t2 可能会在 t1 之前完成,在这种情况下,上述问题再次出现,导致程序再次出现未定义的行为。