登录
首页 >  Golang >  Go问答

golang中的函数不会执行ctx.Done,即使上下文已传递

来源:stackoverflow

时间:2024-03-15 22:42:30 332浏览 收藏

Go 语言中的函数可能不会执行 `ctx.Done()`,即使上下文已传递。这是因为 goroutine 可能阻塞在默认分支中,无法响应上下文取消信号。解决方法是将发送操作从默认分支移至 `select` case。此外,如果需要等待 goroutine 完成,可以使用 `sync.WaitGroup` 来确保在取消上下文后 goroutine 已完成执行。

问题内容

我只是不明白为什么 ctx.done() 没有被执行,即使我正在传递上下文并从主函数调用取消?我在这里做错了什么?

var c = make(chan string)

func A(ctx context.Context) {
 for {
    select {
    case <-ctx.Done():
        fmt.Println("killing AAAA")
        return // kill A at least
    default:
        fmt.Println("in A1.. .. again")
        c <- "yesss"
    }
 }
}

//func B(ctx context.Context) {

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    fmt.Println("BEFORE Number of active goroutines ", runtime.NumGoroutine())
    go A(ctx)
    time.Sleep(2 * time.Second)
    valueReceived := <-c
    cancel()
    fmt.Println("AFTER Number of active goroutines ", runtime.NumGoroutine())
}

正确答案


goroutine 执行默认分支两次并在发送到 c 时阻塞。 <-ctx.done() 案例没有执行,因为 goroutine 卡在默认分支中。

通过从 select case 而不是分支语句发送来解决该问题。

func a(ctx context.context) {
    for {
        select {
        case <-ctx.done():
            fmt.println("killing aaaa")
            return // kill a at least
        case c <- "yesss":
            fmt.println("in a1.. .. again")
        }
    }
}

仅通过此更改,您可能看不到 killing aaaa,因为程序可以在 goroutine 运行完成之前退出。

等待 goroutine 完成才能看到消息:

var wg sync.WaitGroup

func A(ctx context.Context) {
    defer wg.Done()
    for {
        select {
        case <-ctx.Done():
            fmt.Println("killing AAAA")
            return // kill A at least
        case c <- "yesss":
            fmt.Println("in A1.. .. again")
        }
    }
}

...

wg.Add(1)
go A(ctx)
time.Sleep(2 * time.Second)
valueReceived := <-c
cancel()
wg.Wait()

Run it on the Go playground

文中关于的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《golang中的函数不会执行ctx.Done,即使上下文已传递》文章吧,也可关注golang学习网公众号了解相关技术文章。

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>