首页 > 解决方案 > 从孩子取消上下文

问题描述

我对 Golang 比较陌生,正在尝试将 Contexts 合并到我的代码中。

我看到了从父级取消以及共享特定于上下文的内容(例如记录器)方面的好处。

除此之外,我可能会遗漏一些东西,但我看不到孩子取消上下文的方法。此处的示例是,如果其中一个子例程遇到错误,则意味着整个上下文已完成。

这是一些示例代码:

package main

import (
    "context"
    "fmt"
    "math/rand"
    "os"
    "os/signal"
    "sync"
    "time"
)

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

    // handle SIGINT (control+c)
    go func() {
        c := make(chan os.Signal, 1)
        signal.Notify(c, os.Interrupt)
        <-c
        fmt.Println("main: interrupt received. cancelling context.")
        cancel()
    }()

    wg := sync.WaitGroup{}

    wg.Add(1)
    go func() {
        child1DoWork(ctx)
        wg.Done()
    }()
    wg.Add(1)
    go func() {
        child2DoWork(ctx)
        wg.Done()
    }()
    fmt.Println("main: waiting for children to finish")
    wg.Wait()
    fmt.Println("main: children done. exiting.")
}

func child1DoWork(ctx context.Context) {
    // pretend we're doing something useful
    tck := time.NewTicker(5 * time.Second)
    for {
        select {
        case <-tck.C:
            fmt.Println("child1: still working")
        case <-ctx.Done():
            // context cancelled
            fmt.Println("child1: context cancelled")
            return
        }
    }
}

func child2DoWork(ctx context.Context) {
    // pretend we're doing something useful
    tck := time.NewTicker(2 * time.Second)
    for {
        select {
        case <-tck.C:
            if rand.Intn(5) < 4 {
                fmt.Println("child2: did some work")
            } else {
                // pretend we encountered an error
                fmt.Println("child2: error encountered. need to cancel but how do I do it?!?")
                // PLACEHOLDER: HOW TO CANCEL FROM HERE?
                return
            }
        case <-ctx.Done():
            // context cancelled
            fmt.Println("child2: context cancelled")
            return
        }
    }
}

在这里,您有一个从父级取消的示例(由于 SIGINT),效果很好。但是,在 child2DoWork 中有一个占位符遇到错误,然后我想取消整个上下文,但是我看不到使用 vanilla 上下文功能的方法。

这超出了上下文的范围吗?显然,我可以从 child2 与父母沟通,然后可以取消,但我想知道是否没有更简单的方法。

如果与父母沟通是正确的方式,是否有一种惯用的方式来做到这一点?这似乎是一个普遍的问题。

谢谢!

标签: goconcurrency

解决方案


孩子不能也不应该取消上下文,这是父母的电话。孩子可以做的是返回一个错误,父母应该决定这个错误是否需要取消上下文。

仅仅因为“子任务”失败,并不意味着所有其他子任务都需要取消。通常,失败的子任务可能意味着其他子任务变得更加重要。想想并行搜索:您可以使用多个子任务在多个来源中搜索相同的东西。您可以使用最快的结果并可能希望取消较慢的结果。如果搜索失败,您确实希望其余的继续。

显然,如果你将取消函数传递给孩子,孩子将有权取消上下文。而是将这种权力留给父母。


推荐阅读