在Go语言中处理取消的上下文

piv4azn7  于 2022-12-07  发布在  Go
关注(0)|答案(1)|浏览(146)

我希望能干净地处理一个上下文,它可以在我的程序中被取消。我将建模它的 backbone 。

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// tracks errors or successes
completionChannel := make(chan bool)

go func1(ctx)
go func2(ctx)

numberOfFunctions := 2
for i := 0; i < numberOfFunctions; i++ {
 
 // async signal to know if channel was successful or not
 result <- completionChannel
 if !result {

   // should cancel all further processing of the go routines initialized above
   cancel()
   return
 }
}
  • completionChannel用布尔值填充(假=失败,真=成功)
  • 如果从通道读取的值为假,则取消所有正在运行的go例程
func func1(ctx) {
   retry := true
   while (retry) {

      // if context is cancelled, then terminate early (how do I do this elegantly?)

      err := doThing()
      if err.Status == 404 {
        retry = false
      }
   }
}

想象一下func1func2几乎是一样的。它们一遍又一遍地做一件事(想想指数回退)。我想阻止迭代继续,如果上下文被取消以防止内存泄漏。因为阅读一个没有被取消的通道是阻塞的,我该如何在注解所在的地方添加它呢?

编辑:

我可以只添加一个非阻塞选择吗?

select {
        case <-ctx.Done():
            fmt.Println("returning!")
            return
        default:
        }
fkaflof6

fkaflof61#

看看这个

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    wg := sync.WaitGroup{}
    wg.Add(2)
    go func1(ctx, cancel, &wg)
    time.Sleep(time.Second * 1)
    go func1(ctx, cancel, &wg)

    wg.Wait()
    fmt.Println("All goroutines are closed")
}

func func1(ctx context.Context, cancel context.CancelFunc, wg *sync.WaitGroup) {
    select {
    case <-ctx.Done():
        fmt.Println("Goroutine closed by context cancel status")
        wg.Done()
        return
    default:
        status := doThing()
        if status == 404 {
            fmt.Println("Goroutine closed by 404 status")
            cancel()
            wg.Done()
            return
        }
    }
}

func doThing() int {
    return 404
}

这里我修改了doThing函数,以便在状态失败时测试它
我也使用了sync.WaitGroup来等待其他goroutine的结束
要关闭所有其他goroutine,你还需要为它们提供cancel函数
如果你需要跟踪状态,你可以简单地创建completionChannel,把所有的状态都推到那里(注意chan的长度(它将是length == routines count))。或者用slice把信息推到那里,在所有的goroutine退出后读取它。不要在附加信息的过程中读取它-- in可能会导致竞态条件我想是的

相关问题