【发布时间】: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