如何在Golang中取消长时间运行的超时Goroutine?

问题描述 投票:0回答:1

我正在学习如何在 Golang 中使用 context.WithTimeout 在超时后取消长时间运行的操作。这是我的代码的简化版本:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

go func() {
    success := make(chan struct{})
    f := func() {
        // business logic
        success <- struct{}{}
    }
    
    go f() // problematic point
    
    select {
    case <-success:
        fmt.Println("Business completed")
    case <-ctx.Done():
        fmt.Println("Task cancelled")
        return
    }
}()

这个想法是创建一个超时上下文并在一个单独的 goroutine 中(在最外面的 goroutine 内)处理业务逻辑。我想通过将业务逻辑包装在函数

f
中来响应超时信号。一旦
f
完成,它会发送成功信号,最外层的 goroutine 退出。如果发生超时,最外层的goroutine也应该退出。

问题:

  • 如果我先调用
    f
    ,然后等待
    select
    select
    将会被
    f
    阻塞,意味着它无法响应超时信号。
  • 如果我先放置
    select
    ,然后调用
    f
    select
    会阻塞
    f
    ,所以业务逻辑不会执行。
  • 如果我在单独的 Goroutine 中运行
    f
    ,最外层的 Goroutine 会响应超时信号并退出,但
    f
    Goroutine 会继续运行,这就达不到取消的目的。
  • 即使我为
    select
    创建了一个单独的goroutine,它也只会终止
    select
    块,但是正在运行的
    f
    的goroutine仍然会继续执行。

问题:

如何构造代码以便在超时发生时 f 也被取消?

我尝试将业务逻辑(f)放在一个单独的 Goroutine 中,以便最外层的 Goroutine 可以使用 context.WithTimeout 处理超时。我预计如果发生超时,最外面的 goroutine 和 f goroutine 都会被取消。然而,只有最外层的 goroutine 在超时时退出,而 f goroutine 继续运行,这不是期望的行为。

go
1个回答
0
投票

正如其他人已经说过的,你的 goroutine 必须定期合作并检查上下文取消:

f := func() {
        // business logic stage 1
        if ctx.Err()!=nil {
           return
        }
        // business logic state 2
        if ctx.Err()!=nil {
            return
        }
        success <- struct{}{}
}

然后可以选择超时和完成:

select {
    case <-success:
        fmt.Println("Business completed")
    case <-ctx.Done():
        fmt.Println("Task cancelled")
        return
}

您可能还想等到 goroutine 实际返回后再从外部 goroutine 返回。为此,您可以:

done := make(chan struct{})
f:=func() {
   defer close(done)
    .... // rest of f()
}

然后做:

select {
    case <-success:
        fmt.Println("Business completed")
    case <-ctx.Done():
        fmt.Println("Task cancelled")
        <-done
        return
}
© www.soinside.com 2019 - 2024. All rights reserved.