Подтвердить что ты не робот

Как выйти из программы, посвященной отсроченным вызовам?

Мне нужно использовать defer для бесплатных распределений, созданных вручную с помощью библиотеки C, но мне также нужно os.Exit с не-статусом в какой-то момент. Сложная часть состоит в том, что os.Exit пропускает любую отложенную инструкцию:

package main

import "fmt"
import "os"

func main() {

    // `defer`s will _not_ be run when using `os.Exit`, so
    // this `fmt.Println` will never be called.
    defer fmt.Println("!")
    // sometimes ones might use defer to do critical operations
    // like close a database, remove a lock or free memory

    // Exit with status code.
    os.Exit(3)
}

Игровая площадка: http://play.golang.org/p/CDiAh9SXRM украден из https://gobyexample.com/exit

Итак, как выйти из программы go, выполняющей объявленные объявленные вызовы defer? Есть ли альтернатива os.Exit?

4b9b3361

Ответ 1

runtime.Goexit() - это простой способ выполнить это.

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

Однако:

Вызов Goexit из главного goroutine завершает работу goroutine без возврата func main. Поскольку func main не вернулся, программа продолжает выполнение других goroutines. Если все остальные goroutines выходят, программа выйдет из строя.

Итак, в верхней части main вам просто нужно добавить

defer os.Exit(0)

Или, возможно, какая-то другая очистка, чтобы уведомить других горутов о том, что время для остановки, и вы хорошо.

Ответ 2

После некоторых исследований обратитесь к этому this, я нашел альтернативу, которая:

Мы можем использовать panic и recover. Оказывается, что panic, по своей природе, будет чтить defer вызовы, но также будет всегда выходить с кодом статуса 0 и выгружать трассировку стека. Фокус в том, что мы можем переопределить последний аспект панического поведения с помощью:

package main

import "fmt"
import "os"

type Exit struct{ Code int }

// exit code handler
func handleExit() {
    if e := recover(); e != nil {
        if exit, ok := e.(Exit); ok == true {
            os.Exit(exit.Code)
        }
        panic(e) // not an Exit, bubble up
    }
}

Теперь, чтобы выйти из программы в любой точке и сохранить любую объявленную инструкцию defer, нам просто нужно исправить тип Exit:

func main() {
    defer handleExit() // plug the exit handler
    defer fmt.Println("cleaning...")
    panic(Exit{3}) // 3 is the exit code
}

Он не требует каких-либо рефакторинга, кроме подключения строки внутри func main:

func main() {
    defer handleExit()
    // ready to go
}

Это хорошо масштабируется с большими кодовыми базами, поэтому я оставлю его доступным для проверки. Надеюсь, что это поможет.

Игровая площадка: http://play.golang.org/p/4tyWwhcX0-

Ответ 3

Просто переместите свою программу на уровень и верните код выхода:

package main

import "fmt"
import "os"

func doTheStuff() int {
    defer fmt.Println("!")

    return 3
}

func main() {
    os.Exit(doTheStuff())
}

Ответ 4

Для потомков для меня это было более элегантное решение:

func main() { 
    retcode := 0
    defer func() { os.Exit(retcode) }()
    defer defer1()
    defer defer2()

    [...]

    if err != nil {
        retcode = 1
        return
    }
}