重启 Go 协程的惯用方式

huangapple go评论76阅读模式
英文:

Idiomatic way to restart a go routine

问题

我有两个go例程执行两个长时间运行的任务。这些例程可能会出现一些条件错误。我正在尝试找到一种方法,在这些例程出错后重新启动它们。我还必须避免在例程被取消时重新启动。

我现在添加了以下代码。请建议这是否是一种惯用的方法。

func f1(ctx context.Context) error {
    fmt.Println("[f1] start")
    select {
    case <-ctx.Done():
        fmt.Println("[f1] graceful exit")
        return nil
    }
}

func f2(ctx context.Context) error {
    fmt.Println("[f2] start")
    select {
    case <-time.After(1 * time.Second):
        // 模拟错误条件
        fmt.Println("[f2] errored exit")
        return fmt.Errorf("error")
    case <-ctx.Done():
        fmt.Println("[f2] graceful exit")
        return nil
    }
}

func manage(ctx context.Context, f func(ctx context.Context) error) {
    for {
        err := f(ctx)
        if err == nil {
            return
        }
        <-time.After(1 * time.Second)
    }
}

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

    go manage(ctx, f1)
    go manage(ctx, f2)

    // 延迟以查看执行情况
    <-time.After(5 * time.Second)
    cancel()
    <-time.After(5 * time.Second)
}
英文:

I have two go routine performing two long running task. There are a few conditions were these routines can error out. I am trying to see if there is a way to restart the routines once they error out. I have to also avoid a restart if the routine is cancelled.

I have added the below code now. Please suggest if this is an idiomatic way to do this.

func f1(ctx context.Context) error {
    fmt.Println(&quot;[f1] start&quot;)
    select {
    case &lt;-ctx.Done():
        fmt.Println(&quot;[f1] graceful exit&quot;)
        return nil
    }
}

func f2(ctx context.Context) error {
    fmt.Println(&quot;[f2] start&quot;)
    select {
    case &lt;-time.After(1 * time.Second):
        // simulating an error condition
        fmt.Println(&quot;[f2] errored exit&quot;)
        return fmt.Errorf(&quot;error&quot;)
    case &lt;-ctx.Done():
        fmt.Println(&quot;[f2] graceful exit&quot;)
        return nil
    }
}

func manage(ctx context.Context, f func(ctx context.Context) error) {
    for {
        err := f(ctx)
        if err == nil {
            return
        }
        &lt;-time.After(1 * time.Second)
    }
}

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

    go manage(ctx, f1)
    go manage(ctx, f2)

    // delay to see the exeution
    &lt;-time.After(5 * time.Second)
    cancel()
    &lt;-time.After(5 * time.Second)
}

答案1

得分: 5

没有一种惯用的方法来做到这一点。Go协程不会随意死亡并需要重新启动。

要么在go协程内部处理错误并防止其结束,要么将错误传递回启动该协程的点,在那里处理错误,然后根据需要使用不同的输入重新运行go协程。

英文:

There is no idiomatic way to do this. Go routines don't arbitrarily die and have need of restarting.

Either handle the error inside the go routine and prevent it from ending, or communicate the error back to the point the routine was started, handle it there, and then re-run the go routine on different inputs, if appropriate.

huangapple
  • 本文由 发表于 2021年7月11日 18:56:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/68335540.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定