2015-10-20 2 views
0

Есть ли способ выйти из программы 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

Нет, но вы можете реорганизовать свой код, чтобы он мог закончить изящество, а затем отсрочка будет выполнена. Я рекомендую объявить 'c' в верхней части main и передать его в подпрограммы go. В стороне цикла 'for' вам нужен оператор select, который прослушивает' c', если вы получите сигнал на нем, остановите то, что вы делаете, и вернитесь. Тогда вам не нужно использовать 'os.Exit', чтобы убивать ваших goroutines (как вам никогда не нужно ...), и ваша программа может вернуться, что позволяет вашей отложенной' os.RemoveAll' очищать. – evanmcdonnal

ответ

1

Из ссылки golang:

А "отложить" заявление вызывает функцию выполнение которого откладывается на момент окружающей функции возвращает

Когда вы вызываете os.Exit (0), вы обходите обычную процедуру возврата, и ваши отложенные функции не выполняются.

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

Лучшая архитектура кода позволит вам получить что-то подобное. Вы должны думать о своих длительных процессах как о рабочих. Экспортируйте каждый длительный процесс в рабочих и откладывайте очистку сразу после вызова работника. Используйте выберите в главном 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

Лучший ответ, чем я писал в то время, когда мне потребовалось написать его. Тем не менее, выяснение этого было забавным. Для полноты вы можете добавить цитаты из спецификации о том, что происходит с [non-main goroutines] (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 
    } 
} 
Смежные вопросы