有没有办法退出Go程序,但执行所有挂起的延迟语句?
我一直在使用defer清理临时文件,但是当程序被Ctrl + C或os.Exit中断时,不会执行延迟语句。
使用Ctrl + C退出此程序后,foo.txt和bar.txt都会遗留下来:
package main
import (
"fmt"
"io/ioutil"
"os"
"os/signal"
"syscall"
)
func main() {
ioutil.WriteFile("./foo.txt", []byte("foo"), 0644)
defer os.RemoveAll("./foo.txt")
go func() {
ioutil.WriteFile("./bar.txt", []byte("bar"), 0644)
defer os.RemoveAll("./bar.txt")
for {
// various long running things
}
}()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, syscall.SIGTERM)
go func() {
<-c
fmt.Println("Received OS interrupt - exiting.")
os.Exit(0)
}()
for {
// various long running things
}
}
答案 0 :(得分:1)
来自golang参考:
“defer”语句调用其执行被延迟到的函数 周围功能返回
的那一刻
当你调用os.Exit(0)时,你绕过正常的返回程序,你的延迟函数不会被执行。
此外,即使延迟在主要的goroutine内部工作,其他goroutine中的延迟也不会起作用,因为它们会在返回之前死亡。
更好的代码架构可以让您获得类似的东西。您需要将长时间运行的流程视为工作人员。在工作人员中导出每个长时间运行的进程,并在调用该工作人员后立即推迟清理。使用主goroutine中的选择来捕获信号并同步工作
package main
import (
"fmt"
"io/ioutil"
"os"
"os/signal"
"syscall"
"time"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
ioutil.WriteFile("./foo.txt", []byte("foo"), 0644)
defer os.RemoveAll("./foo.txt")
// Worker 1
done := make(chan bool, 1)
go func(done chan bool) {
fmt.Println("worker 1 with bar ...")
ioutil.WriteFile("./bar.txt", []byte("bar"), 0644)
// various long running things
time.Sleep(3 * time.Second)
done <- true
}(done)
defer os.RemoveAll("./bar.txt")
// End worker1
s := make(chan os.Signal, 1)
signal.Notify(s, os.Interrupt)
signal.Notify(s, syscall.SIGTERM)
// Worker 2
done2 := make(chan bool, 1)
go func(done chan bool) {
fmt.Println("worker 2 ...")
time.Sleep(6 * time.Second)
done <- true
}(done2)
// End worker 2
select {
case <-s:
fmt.Println("Quiting with signal - exit")
case <-done:
<-done2
case <-done2:
<-done
}
return
}
这种选择是处理两个工作人员的快捷方式,更好的方法是使用sync.WaitGroup
答案 1 :(得分:0)
我建议不要依赖延迟,而是定义一个可以在延迟或信号块中使用的可重用函数。像这样:
package main
import (
"fmt"
"io/ioutil"
"os"
"os/signal"
"syscall"
)
func main() {
ioutil.WriteFile("./foo.txt", []byte("foo"), 0644)
cleanup := func(){
os.RemoveAll("./foo.txt")
os.RemoveAll("./bar.txt")
}
defer cleanup() //for normal return
go func() {
ioutil.WriteFile("./bar.txt", []byte("bar"), 0644)
for {
// various long running things
}
}()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, syscall.SIGTERM)
go func() {
<-c
fmt.Println("Received OS interrupt - exiting.")
cleanup()
os.Exit(0)
}()
for {
// various long running things
}
}