Как функция вызывающей стороны восстанавливается после паники дочерней горутины

Раньше я думал, что паника в горутине убьет программу, если ее вызывающая программа завершится до паники (отложенное восстановление не помогает, поскольку в этот момент паники еще нет),

пока я не попробовал следующий код:



    func fun1() {
        fmt.Println("fun1 started")
        defer func() {
            if err := recover(); err != nil {
                fmt.Println("recover in func1")
            }
        }()

        go fun2()

        time.Sleep(10 * time.Second) // wait for the boom!
        fmt.Println("fun1 ended")
    }

    func fun2() {
        fmt.Println("fun2 started")

        time.Sleep(5 * time.Second)
        panic("fun2 booom!")

        fmt.Println("fun2 ended")
    }

Я обнаружил, что независимо от того, завершается функция вызывающей стороны или нет, если горутины начинают паниковать, механизм отложенного восстановления вызывающей стороны не поможет. Вся программа по-прежнему мертва.

Итак, ПОЧЕМУ? Теоретически вызывающая функция все еще работает. Когда случаются паники, отложенные функции вызывающей стороны должны работать (включая восстановление).


person Dave Wu    schedule 14.12.2017    source источник


Ответы (2)


В спецификации говорится:

При выполнении функции F явный вызов panic или паники во время выполнения завершает выполнение F. Любые функции, отложенные F, затем выполняются как обычно. Затем запускаются любые отложенные функции, запускаемые вызывающей стороной F, и так далее до любых отложенных функций верхнего уровня в исполняемой горутине. В этот момент программа завершается и сообщается об ошибке, включая значение аргумента для паники. Эта последовательность завершения называется паникой.

Поскольку fun2 является функцией верхнего уровня, выполняемой в горутине, а fun2 не восстанавливается после паники, программа завершается, когда fun2 паникует.

Отложенный вызов в fun1 не вызывается, когда горутина, выполняющая fun2, паникует.

Горутина не может оправиться от паники в другой горутине.

person Cerise Limón    schedule 14.12.2017
comment
Таким образом, это означает, что отложенное восстановление вызывающей стороны вызывается после паники в дочерней горутине, однако функция recovery() в ней не может получить никакой ошибки, отличной от nil, поскольку причина готурина не может восстановиться после паники другой горутины --- это имеет смысл. - person Dave Wu; 15.12.2017
comment
Отложенная функция в fun1 не вызывается при панике fun2. Отсюда следует, что fun1 не может оправиться от паники в fun2. - person Cerise Limón; 15.12.2017
comment
Но только что упомянутая спецификация: Далее запускаются любые отложенные функции, запущенные вызывающей программой F - person Dave Wu; 15.12.2017
comment
... и так далее до любой отложенной функции верхнего уровня в исполняемой горутине. - person Cerise Limón; 15.12.2017
comment
@DaveWu Я думаю, что func1 не звонит func2. func2 работает в другой программе go. - person Viet Tran; 13.07.2018
comment
+1 @VietTran, горутины func1 и func2 выполняются на одном уровне, поэтому, когда одна из них паникует, происходит разматывание стека горутины, и если он достигает вершины горутины, вся программа умирает. Итак, в вашем примере func2 паникует, и среда выполнения завершает работу всей программы. - person Krishna Gupta; 29.06.2021

Вместо восстановления в fun1() вы можете использовать runtime.Goexit() в fun2() который будет

Goexit завершает вызывающую его горутину. Никакая другая горутина не затрагивается.

Что-то типа

func fun2() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("Do some cleanup and teardown")
            runtime.Goexit() //Here
        }
    }
    ...
}
person Uvelichitel    schedule 14.12.2017
comment
Однако обратите внимание, что это предотвратит выполнение отложенных вызовов в вызывающей программе fun2(), даже если они находятся в одной горутине. Например, если вместо этого путь вызова был fun1() -> go fun3() -> fun2(), то вызов runtime.Goexit() в fun2(), даже при отложенном восстановлении, предотвратит запуск любых отложенных вызовов в fun3(). - person Kaedys; 14.12.2017