Обработка паники в подпрограммах go

Я понимаю, что для обработки паники используется восстановление. Но следующий блок не может восстановиться, когда в рутине go возникает паника.

func main() {
    done := make(chan int64)
    defer fmt.Println("Graceful End of program")
    defer func() {
     r := recover()
     if _, ok := r.(error); ok {
        fmt.Println("Recovered")
     }
    }()

    go handle(done)
    for {
        select{
        case <- done:
        return
        }
    } 
}

func handle(done chan int64) {
    var a *int64
    a = nil

    fmt.Println(*a)
    done <- *a
}

Однако следующий блок может выполняться, как и ожидалось.

func main() {
    done := make(chan int64)
    defer fmt.Println("Graceful End of program")
    defer func() {
     r := recover()
     if _, ok := r.(error); ok {
        fmt.Println("Recovered")
     }
    }()

    handle(done)
    for {
        select{
        case <- done:
        return
        }
    } 
}

func handle(done chan int64) {
    var a *int64
    a = nil

    fmt.Println(*a)
    done <- *a
}

Как оправиться от паники, возникающей в рутине го. Вот ссылка на игровую площадку: https://play.golang.org/p/lkvKUxMHjhi


person Mohit Jain    schedule 18.05.2018    source источник


Ответы (2)


Восстановление работает только при вызове из той же горутины, что и вызов паники. Из блога Go:

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

Вы должны иметь отложенное восстановление в горутине.

https://blog.golang.org/defer-panic-and-recover

Документы / спецификации также включают в себя то же самое:

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

https://golang.org/ref/spec#Handling_panics

person Andrew    schedule 18.05.2018

Я использую следующий способ обработки этого случая, и он работает так, как ожидалось.

package main

import (
    "fmt"
    "time"
)

func main() {
    defer fmt.Println("Graceful End of program")
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered:", r)
        }
    }()

    done := make(chan int64)
    var panicVar interface{}

    go handle(done, &panicVar)

WAIT:
    for {
        select {
        case <-done:
            break WAIT // break WAIT: goroutine exit normally
        default:
            if panicVar != nil {
                break WAIT // break WAIT: goroutine exit panicked
            }

            // wait for goroutine exit
        }

        time.Sleep(1 * time.Microsecond)
    }

    if panicVar != nil {
        panic(panicVar) // panic again
    }
}

func handle(done chan int64, panicVar *interface{}) {
    defer func() {
        if r := recover(); r != nil {
            // pass panic variable outside
            *panicVar = r
        }
    }()

    var a *int64
    a = nil

    fmt.Println(*a)
    done <- *a
}

Ссылка на игровую площадку: https://play.golang.org/p/t0wXwB02pa3

person xia xiongjun    schedule 03.06.2021
comment
вместо использования var для хранения состояния паники используйте каналы. Таким образом, вы можете удалить файл sleep & for in main. В этом случае может служить один только select. - person lokanadham100; 22.07.2021