2015-10-20 32 views
0

有什么办法可以退出Go程序,但执行所有挂起的延迟语句吗?清除临时文件的最佳方法

我一直在使用延迟清理临时文件,但当程序被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

不,但你可以重构你的代码,所以它可以优雅地关闭,然后推迟执行。我建议在main的顶部声明'c'并将它传递给go例程。在for循环中,你需要一个select语句来监听'c',如果你得到一个信号,就停止你正在做的事情并返回。然后你不需要使用'os.Exit'来杀死你的goroutines(因为你从来不应该这么做),你的程序可以正常返回,从而允许你的被攻击的os.RemoveAll进行清理。 – evanmcdonnal

回答

1

从golang参考:

A “推迟” 语句调用一个函数其执行被推迟到 的那一刻周边功能返回

当您调用os.Exit(0)时,您会绕过正常返回过程,并且不会执行延期的功能。

此外,即使推迟在主要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

比我写出来的时间写得更好的答案。尽管如此,搞清楚很有趣。为了完整起见,您可以在程序退出时从规范中添加关于[非主要例程](https://golang.org/ref/spec#Program_execution)会发生什么的引号。 [os.Exit()](https://golang.org/pkg/os/#Exit)同上“程序立即终止;延迟函数不运行。” – AndrewN

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 
    } 
}