为什么如果我们不使用 join() 多线程 C++ 程序会崩溃,而类似的 Java 程序不会
Why a multithreading C++ program crashes if we don't use join(), while a similar Java program doesn't
假设我们有以下 C++ 程序
void hello (){
std :: cout << "HELLO"<<std::endl ;
}
int main(){
std:: thread t(hello) ;
t.join() ;
}
如果我们不在这段代码中调用 join,我们的程序就会崩溃,因为主线程会在线程 t1 完成之前终止。
但是如果我们在 Java 中有相同的程序,即使 main 不等待线程,程序也会正常执行。
public class HelloWorld {
public static void main(String[] args) {
Thread t = new Thread(new Hello());
t.start();
}
}
class Hello implements Runnable {
public void run() {
System.out.println("Hello") ;
}
}
那么为什么在 Java 中程序没有崩溃?即使 main 先完成,线程如何执行?
这很简单:与 C++ 相比,一旦 main
完成就终止,java 程序只有在所有(非守护进程)线程(包括 main)都完成时才结束(cf ., 例如 this oracle 线程文档):
When a Java Virtual Machine starts up, there is usually a single
non-daemon thread (which typically calls the method named main of some
designated class). The Java Virtual Machine continues to execute
threads until either of the following occurs:
a. The exit method of class Runtime has been called and the security
manager has permitted the exit operation to take place.
b. All threads that are not daemon threads have died, either by
returning from the call to the run method or by throwing an exception
that propagates beyond the run method.
相比之下,C++ 将开始破坏具有静态存储持续时间的对象,如果分离的线程仍然是 运行 并访问这些对象,这会产生未定义的行为(例如,this关于程序终止的C++标准草案):
3.6.3 Termination
Destructors ([class.dtor]) for initialized objects (that is, objects
whose lifetime ([basic.life]) has begun) with static storage duration
are called as a result of returning from main and as a result of
calling std::exit ([support.start.term]).
这是因为 JVM 的进程终止规则与 C/C++ 不同。在 JVM 上,一旦不再有非守护线程 运行,进程就会终止。 C 或 C++ 应用程序在其 main
函数 returns 后终止。见 If you return from the main thread, does the process exit?:
the C runtime library automatically calls ExitProcess when you exit the main thread, regardless of whether there are any worker threads still active. This behavior for console programs is mandated by the C language, which says that (5.1.2.2.3) "a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument." The C++ language has an equivalent requirement (3.6.1). Presumably, the C runtime folks carried this behavior to WinMain for consistency.
C 的行为就像您在 Java main
.
的末尾调用 System.exit
直接答案如下:您的应用程序崩溃是因为如果线程未加入且未分离,std::thread
在其析构函数中调用 std::terminate
。它被认为是一个讨厌的错误,默默地忘记非分离线程。
如果在从 main 返回之前分离线程,可以避免立即崩溃。但是,由于访问 std::cout
- 这是一个全局对象,并且在从 main
返回后将被销毁,可能在您的线程仍在访问时,您仍然可能会遇到各种可能的烟火它。
假设我们有以下 C++ 程序
void hello (){
std :: cout << "HELLO"<<std::endl ;
}
int main(){
std:: thread t(hello) ;
t.join() ;
}
如果我们不在这段代码中调用 join,我们的程序就会崩溃,因为主线程会在线程 t1 完成之前终止。 但是如果我们在 Java 中有相同的程序,即使 main 不等待线程,程序也会正常执行。
public class HelloWorld {
public static void main(String[] args) {
Thread t = new Thread(new Hello());
t.start();
}
}
class Hello implements Runnable {
public void run() {
System.out.println("Hello") ;
}
}
那么为什么在 Java 中程序没有崩溃?即使 main 先完成,线程如何执行?
这很简单:与 C++ 相比,一旦 main
完成就终止,java 程序只有在所有(非守护进程)线程(包括 main)都完成时才结束(cf ., 例如 this oracle 线程文档):
When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:
a. The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
b. All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.
相比之下,C++ 将开始破坏具有静态存储持续时间的对象,如果分离的线程仍然是 运行 并访问这些对象,这会产生未定义的行为(例如,this关于程序终止的C++标准草案):
3.6.3 Termination
Destructors ([class.dtor]) for initialized objects (that is, objects whose lifetime ([basic.life]) has begun) with static storage duration are called as a result of returning from main and as a result of calling std::exit ([support.start.term]).
这是因为 JVM 的进程终止规则与 C/C++ 不同。在 JVM 上,一旦不再有非守护线程 运行,进程就会终止。 C 或 C++ 应用程序在其 main
函数 returns 后终止。见 If you return from the main thread, does the process exit?:
the C runtime library automatically calls ExitProcess when you exit the main thread, regardless of whether there are any worker threads still active. This behavior for console programs is mandated by the C language, which says that (5.1.2.2.3) "a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument." The C++ language has an equivalent requirement (3.6.1). Presumably, the C runtime folks carried this behavior to WinMain for consistency.
C 的行为就像您在 Java main
.
System.exit
直接答案如下:您的应用程序崩溃是因为如果线程未加入且未分离,std::thread
在其析构函数中调用 std::terminate
。它被认为是一个讨厌的错误,默默地忘记非分离线程。
如果在从 main 返回之前分离线程,可以避免立即崩溃。但是,由于访问 std::cout
- 这是一个全局对象,并且在从 main
返回后将被销毁,可能在您的线程仍在访问时,您仍然可能会遇到各种可能的烟火它。