信号goroutine在通道关闭时停止

时间:2018-11-12 21:43:51

标签: go channel

我从两个通道获得了多个select的goroutine:一个通道提供数据,一个通道提供信号(完成/退出通道的种类)。

我使用信号通道捕获信号(杀死)并优雅地关闭goroutines。

我正在从package a运行'worker'goroutine,而捕获信号的goroutine func从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()

的goroutine
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的打印消息。我希望关闭通道后,goroutine程序将在某个时刻select QuitChan情况下返回。

但是他们继续奔跑;他们继续处理WorkChan中的项目。似乎被忽略了。我在这里想念什么? 频道不关闭吗?怎么还开着呢?

1 个答案:

答案 0 :(得分:2)

首先,我认为您应该进行一个简单的测试,然后将其删除。让其他人了解您的问题会有所帮助。

我更改了您的代码,并使其像go代码一样阅读,而不是其他语言。 现在可以了

在您的代码中,存在一些错误,我将其标记为ERROR注释。有些是语法错误,例如创建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.