Сигнальные программы останавливаются при закрытии канала - PullRequest
0 голосов
/ 13 ноября 2018

У меня есть несколько процедур, которые select по двум каналам: один канал предоставляет данные, один канал для сигналов (вид «Готово / выход из канала»).

Я использую канал сигналов, чтобы захватывать сигналы (убивать) и грациозно закрывать программы.

Я запускаю "рабочие" подпрограммы из package a, в то время как функция подпрограммы, которая захватывает сигналы, запускается из package b.

Я использую пакет сигналов от https://gist.github.com/reiki4040/be3705f307d3cd136e85.

package a

import "sync"

WorkChan := make(chan int)
QuitChan := make(chan struct{})

func Stop() {
        fmt.Println("Stop called, closing channel")
        close(QuitChan)
}

func Work(wg *sync.WaitGroup) {
    var item int
    for {
        select {
        case item = <- WorkChan:
            ... processing
        case <- QuitChan:
            wg.Done()
            return
        }
    }
}

Программа для перехвата сигналов и вызова a.Stop()

package b

import (
    "os/signal"
    "os"
    "syscal"
    "a"
)

func Signal() {

    sChan := make(chan os.Signal, 1)
    signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT)

    for {
        s := <-sChan
        switch s {
        case os.Interrupt, syscall.SIGTERM:
            a.Stop()
        }
    }
}

а это мой главный функционал

package main

import (
    "a"
    "b"
    "sync"
)

func main() {

    var wg sync.WaitGroup

    go b.Signal()

    wg.Add(1) // for simplicity; actual code start multiple goroutines of Work
    go a.Work(&wg)

    // wait until work is done
    wg.Wait()
    fmt.Println("Done.")
}

Когда я прекращаю запущенный процесс, я вижу напечатанное сообщение от Quit. Я ожидал, что как только канал закроется, в какой-то момент goroutines будет select случай QuitChan и вернется.

Но они продолжают бежать; они продолжают обрабатывать предметы с WorkChan. кажется, что это игнорируется. Что мне здесь не хватает? Разве канал не закрывается? Почему это все еще открыто?

1 Ответ

0 голосов
/ 13 ноября 2018

Сначала я думаю, что вы должны сделать простой тест и пройти его.Это поможет вам понять, в чём ваша проблема.

Я изменил ваш код и сделал его читаемым как код go, а не как другой язык. Теперь это работает .

В вашем коде есть некоторые ошибки, я пометил это как комментарий ОШИБКИ.Некоторые из них грамматические ошибки, такие как создание WorkChan.Некоторые из них являются ошибкой типа.

Одна вещь, связанная с дизайном импорта, которую вы должны знать: когда вы хотите выйти после выполнения Stop(), вы должны закрыть WorkChan, куда вы отправляете данные в WorkChan, вместо того, чтобы просто вернуться вгде вы получаете дату.

  • a.go

    package a
    
    import (
        "fmt"
        "sync"
    )
    
    // ERROR: can not do make in global
    var WorkChan chan int
    var QuitChan chan struct{}
    
    // Create chan when init
    func init() {
        fmt.Println("Init a")
        WorkChan = make(chan int)
        QuitChan = make(chan struct{})
    }
    
    func Stop() {
        fmt.Println("Stop called, closing quit channel")
        close(QuitChan)
    }
    
    // Close the work channel where you send date
    func Start(wg *sync.WaitGroup) {
        i := 0
        for {
            select {
            case <-QuitChan:
                fmt.Println("Closing work chan")
                close(WorkChan)
                wg.Done()
                return
            default:
                WorkChan <- i
                i++
            }
        }
    }
    
    // Work will exit when workchan closed
    func Work(wg *sync.WaitGroup) {
        for item := range WorkChan {
            fmt.Printf("Receive %d\n", item)
        }
        wg.Done()
        fmt.Println("Work exit")
    }
    
  • b.go

    package b
    
    import (
        "github.com/shitaibin/awesome/a"
        "os"
        "os/signal"
        "syscall"
    )
    
    func Signal() {
    
        sChan := make(chan os.Signal, 1)
        signal.Notify(sChan, syscall.SIGTERM, syscall.SIGINT) // ERROR
    
        for {
            s := <-sChan
            switch s {
            case os.Interrupt, syscall.SIGTERM:
                a.Stop()
                return // should return free resource
            }
        }
    }
    
  • main.go

    package main
    
    import (
        "fmt"
        "github.com/shitaibin/awesome/a"
        "github.com/shitaibin/awesome/b"
        "sync"
    )
    
    func main() {
    
        var wg sync.WaitGroup
    
        go b.Signal()
    
        wg.Add(1)      // for simplicity; actual code start multiple goroutines of Work
        go a.Work(&wg) // ERROR: pointer
    
        wg.Add(1)
        go a.Start(&wg) // Send data and close channel when stop
    
        // wait until work is done
        wg.Wait()
        fmt.Println("Done.")
    }
    
  • Результат

    // omit
    Receive 87028
    Receive 87029
    Receive 87030
    Receive 87031
    Receive 87032
    Receiv^C101    <---- send signal here
    Receive 87102
    Receive 87103
    Receive 87104
    Receive 87105
    Receive 87106
    Receive 87107
    Receive 87108
    Receive 87109
    Receive 87110
    Stop called, closing quit channel
    Receive 87111
    Receive 87112
    Closing work chan
    Work exit
    Done.
    
...