Golang 中 Context.Cancel() 后,
golang 中 context 执行了 cancel,但是
在 golang 中使用 context 来管理子协程时,我们可能会遇到这样的问题:调用 cancel() 方法后,却发现
这种情况通常出现在子协程阻塞在 channel 通信时。以下代码示例可以帮助我们理解这个问题:
package main import ( "context" "fmt" ) func gen(ctx context.context) <-chan interface{} { ch := make(chan interface{}) go func() { n := 0 for { select { case <-ctx.done(): fmt.println("done") default: n += 1 ch <- n } } }() return ch } func main() { ctx, cancel := context.withcancel(context.background()) for n := range gen(ctx) { fmt.println(n) if n == 5 { break } } defer cancel() }
在这个示例中,我们希望在打印出数字 5 时取消子协程,并调用 fmt.println("done")。然而,执行后却发现 "done" 并未打印。
其原因在于,当 cancel() 被调用时,主协程退出,但是子协程仍旧阻塞在 ch
要解决这个问题,我们有两种方法:
1. 关闭 channel
在取消子协程时关闭 channel,这样子协程将在读取 channel 失败后退出。
func gen(ctx context.context) <-chan interface{} { ch := make(chan interface{}) go func() { n := 0 for { select { case <-ctx.done(): fmt.println("done") close(ch) return default: n += 1 ch <- n } } }() return ch }
2. 使用 time.after 和 select
通过使用 time.after 和 select 来处理超时情况。这样,子协程将同时监听 channel 和超时,并在超时或 context 被取消时退出。
func gen(ctx context.Context) <-chan interface{} { ch := make(chan interface{}) go func() { n := 0 ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-ticker.C: fmt.Println("timeout") case <-ctx.Done(): fmt.Println("done") default: n += 1 ch <- n } } }() return ch }
以上就是Golang 中 Context.Cancel() 后,的详细内容,更多请关注硕下网其它相关文章!