上下文因超时而取消但计算未中止?
Posted
技术标签:
【中文标题】上下文因超时而取消但计算未中止?【英文标题】:Context cancelled by timeout but computation is not aborted? 【发布时间】:2021-07-07 00:30:12 【问题描述】:试图了解 go 上下文取消将如何中止后续代码的执行
实验详情:
-
main func 的上下文在
2sec
中超时
main func 在单独的 go-routine 中调用另一个 func sum
- 对于 test-run-1 和 4sec
对于 test-run-2 睡眠为 4sec
让 3sec
的主程序休眠以让 spun go-routine 完成执行
package main
import (
"context"
"fmt"
"log"
"time"
)
func main()
c := context.Background()
childCtx, cancel := context.WithTimeout(c, 2*time.Second)
defer cancel()
ch := make(chan int, 1)
go sum(5, 6, ch)
var msg string
select
case <-childCtx.Done():
msg = "return from ctx done channel"
case res := <-ch:
msg = fmt.Sprintf("return from go routine: %v", res)
log.Print(msg)
time.Sleep(3 * time.Second) //sleeping here to test if go-routine is still running
func sum(x int, y int, c chan<- int)
time.Sleep(1 * time.Second)
//testcase-1: sleep - 1s
//testcase-2: sleep - 4s
result := x + y
log.Printf("print from sum fn: %v", result)
c <- result
对 testcase-1 的响应:睡眠总和功能 1 秒:
2021/04/12 01:06:58 print from sum fn: 11
2021/04/12 01:06:58 return from go routine: 11
对 testcase-2 的响应:睡眠总和功能 4 秒:
2021/04/12 01:08:25 return from ctx done channel
2021/04/12 01:08:27 print from sum fn: 11
在 testcase-2 中 sum func 休眠 4 秒,context 在 2 秒后已经被超时取消,为什么它还在 diff go-routine 中执行 sum func 并打印 print from sum fn: 1
?
来自文档:Canceling this context releases resources associated with it.
我的假设是所有计算将在 2 秒后立即中止,包括旋转的 go-routine
让我知道如何正确执行此操作,提前感谢
【问题讨论】:
拥有上下文并没有什么神奇的。如果你想让你的 goroutine 检测上下文何时完成,你必须让它也尝试从上下文的Done
方法返回的通道接收,就像你在 main
中所做的那样。
一个更通用的说法是:go 的 goroutine 是(并且必须是)合作的。一个 goroutine 不能射击另一个 goroutine 来杀死它,但是一个 goroutine 可以 采取一些行动(例如将上下文标记为已取消,或关闭通道)以便第二个 goroutine 可以注意第一个已经礼貌地要求第二个退出。您必须编写第二个 goroutine 才能注意到礼貌的请求。
感谢您帮助 torek 和 @Andy Schweig
我对事物的理解有点错误,这些 cmets 帮助我纠正了,更多关于这个 - 参考这个博客 - sohamkamani.com/golang/…
【参考方案1】:
正如@AndySchweig 所指出的,context
发出取消事件的信号,但不强制取消。在检测到取消后,由任何可能阻塞的 goroutine 尽最大努力尝试取消/清理。
要更新您的 sum
函数以支持取消,您可以尝试:
// add context parameter as the first argument
// add a return error - to indicate any errors (i.e. function was interrupted due to cancelation)
func sum(ctx context.Context, x int, y int, c chan<- int) (err error)
wait := 1 * time.Second // testcase-1
//wait := 4 * time.Second // testcase-2
// any blocking called - even sleeps - should be interruptible
select
case <-time.After(wait):
case <-ctx.Done():
err = ctx.Err()
return
result := x + y
log.Printf("print from sum fn: %v", result)
select
case c <- result:
case <-ctx.Done(): // check for ctx cancelation here - as no one may be listening on result channel
err = ctx.Err()
return
https://play.golang.org/p/DuIACxPvHYJ
【讨论】:
你能帮我学习GO吗 @shaara 我确定您已经完成了Go Tour - 重复一遍总是好的,因为您可能会在第一次阅读时错过很多内容。然后是Go by example。这是另一个good resource。最后只是代码!没有比“弄脏”双手更好的学习方法了。祝你好运!【参考方案2】:上下文不会中止 go 例程。在您的情况下,如果上下文的时间到了,您就不会打印 go 例程的结果。 go 例程对上下文一无所知。
【讨论】:
以上是关于上下文因超时而取消但计算未中止?的主要内容,如果未能解决你的问题,请参考以下文章
XCUI 测试因异步等待失败而失败:超过 30 秒的超时,未达到预期
可以像中止一个Thread(Thread.Abort方法)一样中止一个Task吗?