Golang中Context cancel()未生效的问题
Golang中Context cancel()未生效的问题 我按照Francesc(justforfunc)关于使用上下文进行简单取消的示例操作,但实际上并没有取消执行?
以下是代码:
package main
import (
"context"
"fmt"
"time"
)
func sleepAndTalk(ctx context.Context, t time.Duration, txt string) {
time.Sleep(t)
fmt.Println(txt)
}
func main() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
go func() {
time.Sleep(time.Second)
cancel()
}()
sleepAndTalk(ctx, 5*time.Second, "yolo")
}
理论上,执行应该在一秒后被取消,但实际上并没有,而是继续等待5秒然后输出文本 yolo。
在Go Playground上似乎也无法正常工作,有谁知道为什么这不起作用吗? https://play.golang.org/p/edsDZkYylrF
谢谢!
更多关于Golang中Context cancel()未生效的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
好的,原来我只是不够耐心,它(还)不能正常工作的原因稍后会有解释 😉。
更多关于Golang中Context cancel()未生效的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
问题在于你的 sleepAndTalk 函数没有检查 ctx.Done() 通道,因此即使上下文被取消,函数仍然会继续执行完整的睡眠和输出操作。context.WithCancel 本身不会自动中断正在执行的函数,你需要显式地检查上下文的取消状态。
以下是修正后的代码:
package main
import (
"context"
"fmt"
"time"
)
func sleepAndTalk(ctx context.Context, t time.Duration, txt string) {
select {
case <-time.After(t):
fmt.Println(txt)
case <-ctx.Done():
fmt.Println("Cancelled:", ctx.Err())
}
}
func main() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
go func() {
time.Sleep(time.Second)
cancel()
}()
sleepAndTalk(ctx, 5*time.Second, "yolo")
}
在这个版本中:
- 使用
select语句同时等待两个通道:time.After(t)和ctx.Done() - 如果上下文在超时前被取消,
ctx.Done()通道会关闭,执行取消分支 - 如果超时先发生,则正常输出文本
运行这个版本,你会看到一秒后输出 “Cancelled: context canceled” 而不是等待5秒后输出 “yolo”。
如果你需要在睡眠期间定期检查取消状态,可以使用以下替代实现:
func sleepAndTalk(ctx context.Context, t time.Duration, txt string) {
for elapsed := time.Duration(0); elapsed < t; elapsed += time.Millisecond * 100 {
select {
case <-ctx.Done():
fmt.Println("Cancelled:", ctx.Err())
return
default:
time.Sleep(time.Millisecond * 100)
}
}
fmt.Println(txt)
}
这个版本每100毫秒检查一次取消状态,提供更及时的响应。

