【问题标题】:Go standard contexts [closed]转到标准上下文[关闭]
【发布时间】:2022-08-22 11:46:16
【问题描述】:

相对较新的 Go 用户,来自 C 和 Python。试图理解上下文,我一直在等待一些神奇的解释来点击,最后对自己说,“哦,这似乎真的很有用”,但每次我看到示例和用法时,它似乎都是一个非常基本的“全局”标志表示已打包并卡入标准库的状态。

我错过了一些关于上下文的特别之处吗?

我觉得两个主要用例是:

  1. 传递任意用户数据(void *usersata 或 def function(args, udata={}))。

  2. 更有用的情况是取消/超时。典型的例子是使用计时器或其他东西来检查它是否在 goroutine 中完成:

    const interval = 500
    
    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        go func() {
            time.Sleep(5 * interval * time.Millisecond)
            cancel()
        }()
        f(ctx)
    }
    
    func f(ctx context.Context) {
        ticker := time.NewTicker(interval * time.Millisecond)
        for {
            select {
            case <-ticker.C:
                doSomething()
            case <-ctx.Done():
                return
            }
        }
    }
    
    func doSomething() { log.Println(\"tick\") }
    

    我明白了,但这是否意味着每个上下文感知函数都在执行这个 for-select 循环以支持检测取消?我有点期待一些内置的围棋魔术。这与任何其他全局标志/互斥体/goroutine 可以检查的任何东西有什么不同if globalFlag == CANCELLED

    此外,在我看到的上下文描述中,“支持通知长时间运行的进程不再需要继续”。然而,这个想法只适用于你的长时间运行的过程实际上是多个可以在 for-select 循环中迭代的东西。同步/阻塞过程永远不能真正被取消,可以吗?如果我有一个长时间运行的 mysql 查询,上下文不会神奇地给我一些方法来断开 mysql 连接并放弃它吗? mysql.Query(\"SELECT SLEEP(500)\") 无法取消,因为在 Query 实际返回之前,我无法围绕它运行选择循环。

    我是正确理解上下文还是我错过了一些重要的东西?

  • 包 mutex+variable 不可用或与其他包 API 标准化,并且不允许诸如选择多个通道之类的事情。是的,您可以“只是”通过其他方式做到这一点——上下文只是对其功能的方便包装,我们多年来一直没有它们,然后它们是通过外部包实现的。
  • 它不是一个全局标志,因为它是明确传递的,这肯定是一件好事。没有魔法全球。
  • 选择语句不是检查取消的唯一方法。您还可以检查Context.Err 的返回值。数据库查询绝对可以被中断,至少通过简单地关闭网络连接(如果内存可用,这就是 MySQL 驱动程序所做的)。
  • \"这个想法只适用于你的长时间运行的过程实际上是多个可以在 for-select 循环中迭代的东西。\"。这是不正确的。在 select 语句之外,您可以使用 Err 方法检查上下文是否关闭。您可以在整个非循环任务中的任何时间点任意执行此操作。

标签: go


【解决方案1】:

我认为您不需要 for 循环来取消。

你只需要一个等待结果的通道。并且可以根据上下文取消。

    select {
    case result := <-ch:
        return result, nil
    case <-ctx.Done():
        return nil, ctx.Err()
    }

简单 - 走的路

在不阻塞代码的情况下,您可以这样做:

 if err := ctx.Err(); err != nil {
     return nil, err
 }

【讨论】:

  • 抱歉,是的,不需要 for 循环。但这仍然不会让您取消运行缓慢的查询,除非您将控制权交还给您以执行此检查,对吗?
  • 是的,你不能杀死 goroutine,如果它不使用 context 并且这个 goroutine 不检查 context.Done() 每个 goroutine 都应该使用 context 并检查 context.Done()
  • 安德烈,我了解用法,但我看不到实用程序。将取消上下文传递给仅执行以下操作的函数如何: mysql.Query("SELECT SLEEP(300)") 提供任何好处?无法取消此功能,因为没有检查上下文的选项。
  • @KaibutsuX:你故意塑造一个实用性有限的例子。您将使用 QueryContext 并传递上下文以取消查询。如果底层 DBMS 或驱动程序无法取消查询,那通常与上下文的实用性无关。
  • 那是我当时最基本的误解。我认为在允许这样做的上下文中发生了一些特别的事情。因此,只要我的“长时间运行”进程没有阻塞,这与在没有上下文的情况下自己检查其他标志没有什么不同
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
  • 2016-12-25
  • 2018-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多