go - 信号 goroutines 在 channel 关闭时停止

标签 go channel

我有多个从两个 channel 选择的 goroutines:一个 channel 提供数据,一个 channel 用于信号(一种完成/退出 channel )。

我使用信号 channel 来捕获信号(杀死)并优雅地关闭 goroutine。

我从 package a 运行“worker”协程,而捕获信号的 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
        }
    }
}

goroutine 捕捉信号,并调用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 的打印消息。我预计一旦 channel 关闭,goroutines 将在某个时候选择 QuitChan case 并返回。

但他们一直在奔跑;他们继续处理来自 WorkChan 的项目。似乎它被忽略了。我在这里错过了什么? channel 不会关闭吗?怎么还开了?

最佳答案

首先我认为你应该做一个简单的测试,然后通过它。让其他人了解您的问题会更有帮助。

我更改了您的代码,使其读起来像 go 代码,而不是其他语言。 现在成功了

在你的代码中,有一些错误,我将其标记为错误注释。有些是语法错误,比如创建WorkChan .有些是类型错误。

你应该知道的一个导入设计的事情,当你想在执行后退出时 Stop() , 你应该关闭 WorkChan您将数据发送到 WorkChan 的位置, instate of just return at where you receive date.

  • 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.
    

关于go - 信号 goroutines 在 channel 关闭时停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53270533/

相关文章:

tcp - 在 Go 中的 EOF 上重新连接 TCP

go - "Matrix multiplication"使用 goroutine 和 channel

go - 关闭由 channel 链接的 goroutine 链的优雅方法是什么?

Golang channel : timeout pattern not work as example

channel - 如何使用discord.py获取所有文本 channel ?

go - 你如何优雅地退出 go uber fx 应用程序

go - 将接口(interface)与 golang 和数据库抽象结合使用

go - 如何在 golang 中使用自签名证书设置 https 服务器

go - 有人可以解释一下为什么 "curl: (7) Failed to connect to 127.0.0.1 port 2000: Connection refused"吗?

戈朗。获取 panic 类型