清除临时文件的最佳方法

时间:2015-10-20 02:38:35

标签: go

有没有办法退出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
    }
}

2 个答案:

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