Golang中Context的cancel方法何时会在代码运行时返回

Golang中Context的cancel方法何时会在代码运行时返回 我想知道如果上下文取消代码正在运行,例如运行到以下情况:

func Test(ctx context.Context){
  c,err := test.test()           //如果上下文取消发生在这里
  if err != nil{                      //这段代码会继续执行吗?
       return nil, fmt.Errorf("test fail", err)
  c2,err := test.test2(ctx)
  if err2 != nil{
      return nil, fmt.Errorf("test2 fail", err)
  }
  // 这段代码会运行吗?
  println("over!")
}

我查阅了上下文文档,但不确定具体情况是怎样的。它会立即结束还是会运行一段代码?


更多关于Golang中Context的cancel方法何时会在代码运行时返回的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

看起来你没有任何使用 ctx 的代码,所以不可能有干扰。

更多关于Golang中Context的cancel方法何时会在代码运行时返回的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这意味着如果发生“上下文取消”,代码将继续执行。如果此函数已经卡住,它将在函数内完成处理过程,但只要函数使用了在上下文中声明的变量,它就会返回一个上下文取消错误吗?

如果你修改运行时库,或者通过C语言进行映射,你也可以从外部“巧妙地”结束协程。

谢谢,但这对我来说可能有点困难。

Go 语言没有管理生命周期的方法(除非它自行结束)。 当然,这是基于原生接口而言。 如果你修改运行时库,或者通过 C 语言进行映射,也可以从外部“巧妙”地结束协程。

谢谢,我在阅读文档时产生了误解。起初,我以为 context 是管理协程生命周期的东西。😀

监控的最简单方式: <-ctx.Done() 或者 if ctx.Err()!=nil{ return } 其他…

如果代码中没有处理逻辑,它就不会停止,例如:

for{
// do work…
}

但如果存在处理 ctx 的逻辑:

for ctx.Err()==nil{
}

如果你简单地将 ctx 视为一个通道,就很容易理解。如果你的代码不监控 ctx 的状态变化,就不会有停止逻辑。

它不像 Kotlin 的 launch

ctx 是一个经验整合工具。在 ctx 出现之前,开发者常常担心如何中断业务处理(例如超时、取消)。

ctx 出现之后,基本上较新的库都能看到使用 ctx 来控制业务处理。(甚至在我看来,如果一个工具库不提供 ctx 控制方法,那么这个工具库就很糟糕)

如果你查看标准库中 ctx 的实现,会发现逻辑非常简单。(前提是你愿意阅读源代码)

在Go语言中,当上下文被取消时,context.ContextDone()通道会关闭,但代码不会自动停止执行。取消上下文只是发出信号,具体的处理取决于代码如何检查上下文状态。

在你的示例中,如果上下文取消发生在c,err := test.test()之后,代码会继续执行,因为这里没有检查上下文状态。只有当代码显式检查ctx.Err()或从ctx.Done()通道接收时,才能感知到取消。

以下是修改后的示例,展示如何正确处理上下文取消:

func Test(ctx context.Context) error {
    // 在可能长时间运行的操作前检查上下文
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
    }
    
    c, err := test.test()
    if err != nil {
        return fmt.Errorf("test fail: %w", err)
    }
    
    // 对于接受上下文参数的操作,它们内部会检查上下文
    c2, err := test.test2(ctx)
    if err != nil {
        return fmt.Errorf("test2 fail: %w", err)
    }
    
    // 再次检查上下文状态
    if err := ctx.Err(); err != nil {
        return err
    }
    
    println("over!")
    return nil
}

或者,如果你想要在上下文取消时立即返回,可以在关键点添加检查:

func Test(ctx context.Context) error {
    // 使用带取消检查的循环
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()
        default:
            // 执行操作
            c, err := test.test()
            if err != nil {
                return fmt.Errorf("test fail: %w", err)
            }
            
            c2, err := test.test2(ctx)
            if err != nil {
                return fmt.Errorf("test2 fail: %w", err)
            }
            
            println("over!")
            return nil
        }
    }
}

关键点:

  1. 上下文取消不会自动停止代码执行
  2. 必须显式检查ctx.Done()ctx.Err()
  3. 接受上下文参数的函数(如test2(ctx))通常会在内部检查上下文状态
  4. 如果不检查上下文,代码会继续执行直到函数自然结束
回到顶部