上下文因超时而取消但计算未中止?

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 睡眠为 4sec3sec 的主程序休眠以让 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吗?

如何取消/中止 jQuery AJAX 请求?

为啥我的产品->存档因“非法尝试在不同上下文中的对象之间建立关系”压缩类型“而失败?

trap