【问题标题】:why context.Context signal affects new goroutine?为什么 context.Context 信号会影响新的 goroutine?
【发布时间】:2019-09-02 16:56:00
【问题描述】:

我正在使用context.Context 来控制多个 goroutine。一个 goroutine 监听 context.Done(),然后立即打印日期字符串。该进程会监听syscall.SIGHUP,它会取消上下文,然后使用之前创建的上下文创建新的 goroutine。

我认为当syscall.SIGHUP 发出时,该过程将打印一个日期字符串,但是得到了两个。

此进程正在ubuntu18.04 上运行。

package main

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go printDate(ctx)

    sg := make(chan os.Signal)
    signal.Notify(sg, syscall.SIGHUP)

    for {
        select {
        case <-sg:
            cancel()
            go printDate(ctx)
        }
    }
}

func printDate(ctx context.Context) {
    select {
    case <-ctx.Done():
        fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
    }
}

我希望输出是

2019-01-02 01:02:03

但实际输出是:

2019-01-02 01:02:03
2019-01-02 01:02:03

【问题讨论】:

  • 看了上下文源码后,我认为原因是第二个goroutine执行后ctx.Done通道关闭。所以这个过程会打印两个日期字符串。
  • 第一个 go 例程触发并等待取消。一旦接收到信号,就会发生两件事; waiting go 例程打印时间并创建一个新的,因为上下文已被取消,所以该例程也会打印。我不明白您为什么只期望打印一次。
  • 之前,我认为ctx.Done,它返回一个通道,会导致第一个监听它的 goroutine 得到一些东西。所以,在调用cancel() 之后,ctx 会发出带有值的Done 信号,并且第一个go 例程会打印日期字符串,同时ctx.Done 的值将被设置为空。最终,第二个 goroutine 无法从ctx.Done 获得信号,因为ctx.Done 的通道是空的,第二个 goroutine 什么也不打印。

标签: go


【解决方案1】:

cancel() 之后,您的ctx 完成并永远保持完成。因此,当您再次调用 go printDate(ctx) 时,您使用 done 上下文调用 printDate 并且案例可以运行,因为 ctx.Done() 返回一个关闭的通道,您可以从一个关闭的通道接收。

【讨论】:

  • 这是我的错误。关闭 channel 后,监听它的 goroutine 会从 channel 获取默认值,而不是阻塞。
猜你喜欢
  • 1970-01-01
  • 2020-07-23
  • 2017-01-26
  • 2014-11-26
  • 2013-05-19
  • 1970-01-01
  • 2014-12-27
  • 2021-12-27
  • 2016-12-09
相关资源
最近更新 更多