Почему многопоточная программа C ++ падает, если мы не используем join (), тогда как аналогичная Java-программа не - PullRequest
0 голосов
/ 16 января 2019

Допустим, у нас есть следующая программа на 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 программа не падает? как выполняется поток, даже если основной заканчивается первым?

Ответы [ 3 ]

0 голосов
/ 16 января 2019

Это связано с тем, что в JVM другие правила завершения процесса, чем в C / C ++. В JVM процесс завершается, когда больше не запущены потоки, не являющиеся демонами. Приложение C или C ++ завершается, когда возвращается его функция main. См. Если вы вернетесь из основного потока, завершится ли процесс? :

библиотека времени выполнения C автоматически вызывает ExitProcess при выходе из основного потока, независимо от того, есть ли еще какие-либо активные рабочие потоки. Такое поведение для консольных программ предписано языком Си, который говорит, что (5.1.2.2.3) «возврат из начального вызова основной функции эквивалентен вызову функции выхода со значением, возвращаемым основной функцией в качестве ее аргумент «. Язык C ++ имеет эквивалентное требование (3.6.1). Предположительно, пользователи среды выполнения C перенесли это поведение в WinMain для согласованности.

Поведение C похоже на то, как если бы вы вызывали System.exit в конце вашего Java main.

0 голосов
/ 16 января 2019

Прямой ответ следующий: ваше приложение падает, потому что std::thread вызывает std::terminate в своем деструкторе, если поток не был присоединен и не отсоединен. Считается неприятной ошибкой, молча забыть о неотделенной ветке.

Вы можете избежать немедленного сбоя, если отсоедините поток перед возвратом из основного. Тем не менее, у вас все еще есть вероятность всевозможных фейерверков из-за доступа к std::cout - глобальному объекту, который будет уничтожен после возврата из main, возможно, пока ваш поток все еще обращается к нему.

0 голосов
/ 16 января 2019

Это довольно просто: в отличие от C ++, который завершается после завершения main, java-программа завершается, только если все (не-deamon) потоки (включая main) завершены (ср., Например, это документация по теме оракула):

Когда запускается виртуальная машина Java, обычно поток не-демона (который обычно вызывает метод с именем main некоторых обозначенный класс). Виртуальная машина Java продолжает выполняться потоков, пока не произойдет одно из следующих событий:

а. Метод выхода класса Runtime был вызван и безопасность менеджер разрешил выполнение операции выхода.

б. Все потоки, которые не являются потоками демонов, умерли возвращаясь из вызова к методу run или выбрасывая исключение который распространяется за пределы метода run.

C ++, напротив, начнет разрушать объекты со статической длительностью хранения, и если отсоединенный поток все еще работает и обращается к таким объектам, это приводит к неопределенному поведению (см., Например, этот C ++ стандарт проект о прекращении программы):

3.6.3 Завершение

Деструкторы ([class.dtor]) для инициализированных объектов (то есть объектов) чье время жизни ([basic.life]) началось) со статической продолжительностью хранения вызываются в результате возврата из основного и в результате вызов std :: exit ([support.start.term]).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...