当前位置: 首页 > 知识库问答 >
问题:

在通道关闭时向goroutines发出停止信号

何嘉运
2023-03-14

我从两个通道中获得了多个选择的goroutine:一个chan提供数据,一个chan提供信号(类似于done/quit通道)。

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

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
        }
    }
}
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将selectthequitchancase并返回。

但他们不停地奔跑;它们继续处理workchan中的项。好像被忽视了。我在这里漏掉了什么?频道不是关闭了吗?怎么还开着?

共有1个答案

吕博耘
2023-03-14

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

我改变了你的代码,让它读起来像go代码,而不是其他语言。现在奏效了。

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

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")
}
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
        }
    }
}
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.
 类似资料:
  • 所有的, 现在,我试图避免的是下面的连接语句代码,其中我将代码包装在goroutine中的错误处理中(类似于以下内容): 相反,如果存在连接问题,我宁愿将整个过程短路,然后立即以错误退出goroutine,我也不愿意担心如何构造代码。我也许可以进一步包装_send_ack并将通道作为函数参数发送--但是如果程序是高度分层的,这就变得不确定了。例如,我可能有一个由几个函数组成的goroutine,每

  • 我试图找到正确的方法来使用工人围棋例程产生的结果,同时在所有工作完成后优雅地退出结果循环。为了说明,我做了以下示例。我的真实世界案例与这个例子略有不同,因为我不知道每个工人围棋例程将返回多少“工作”,显然这些for循环执行固定数量的结果(5)。 我是goroutines和频道的新手,但以下是我所理解的基本租户; 只有发送者才能关闭频道 在通道上执行将继续,直到通道关闭 这个例子是死锁,因为范围循环

  • 我有一个使用Twitter4j启动的Android应用程序,如果我不再需要该流,我正在寻求有关如何停止/关闭它的帮助。我在偏好更改时启动流,但需要根据用户操作在应用程序的其他部分停止它。 以下是我启动流的方式,这是我在文档中找到的典型方式: 任何帮助将不胜感激,谢谢!

  • 问题内容: 我正在尝试停止执行例程,但是我找不到实现此目的的方法。我当时在考虑使用第二个频道,但是如果我从中读取它会阻止它,不是吗?这是一些代码,我希望这些代码可以解释我要做什么。 玩这个东西 问题答案: 实现这一点的方法很少,最简单,最方便的是使用其他渠道,例如: 您也可以研究使用

  • 关闭通道的意思是该通道将不再允许写入数据。这个方法可以让通道数据的接受端知道数据已经全部发送完成了。 package main import "fmt" // 在这个例子中,我们使用通道jobs在main函数所在的协程和一个数据 // 接收端所在的协程通信。当我们数据发送完成后,我们关闭jobs通道 func main() { jobs := make(chan int, 5) d

  • 问题内容: 我正在玩Golang,我创建了这个小应用程序,使用goroutines进行了多个并发的api调用。 当应用程序运行时,调用完成后,该应用程序将卡住,这是有道理的,因为由于通道未关闭,无法退出 范围c 循环。 我不确定在哪种情况下可以更好地关闭该通道。 问题答案: 当没有更多值要发送时,您将关闭通道,因此在这种情况下,所有goroutine已完成。 (请注意,from 仅将反映连接和协议