【问题标题】:Stop child goroutine when parent returns [duplicate]当父母返回时停止子goroutine
【发布时间】:2019-05-21 16:56:03
【问题描述】:

我们有一个主 goroutine,它生成一个父 goroutine,而父 goroutine 又生成一个子 goroutine。

即使在父级返回后,子级 goroutine 仍然运行。这会导致 goroutine 泄漏。

我们如何避免这种情况?

下面我加了一个代码sn-p来模拟下面的 这里的子 goroutine 可以是任何长时间运行的进程,例如 db 查询、api 调用等

Program output: 

In main function -  1
Starting parent function -  2
Starting child function -  3
Child timed out -  3
Completed parent -  2  // Implying that child goroutine is still running with main routine
package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

// WaitGroup used by main to wait for parent goroutine
var wg sync.WaitGroup

// Long duration process time
var duration = 100


func main() {
    fmt.Println("In main function - ", runtime.NumGoroutine())
    wg.Add(1)
    go parentRoutine()
    wg.Wait()
    fmt.Println("Completed parent - ", runtime.NumGoroutine())
}

func parentRoutine() {
    fmt.Println("Starting parent function - ", runtime.NumGoroutine())
    childRes := make(chan int)


        // Spawning child goroutine
    go func() {

               // Here the child is a simulation of a long running process which might take more time than expected timeout. It runs even after parent returns due to timeout


        fmt.Println("Starting child function - ", runtime.NumGoroutine())
        time.Sleep(time.Duration(duration)*time.Second)
        fmt.Println("Child ended - ", runtime.NumGoroutine())
        childRes <- 1
    }()

    select {
    case <-childRes:
        fmt.Println("Child completed - ", runtime.NumGoroutine())
    case <- time.After(time.Duration(3)*time.Second):
        fmt.Println("Child timed out - ", runtime.NumGoroutine())
    }
    wg.Done()
}

【问题讨论】:

  • Goroutines 通常没有“父级”,也不需要有一个“父级”来防止泄漏。您只需要确保您的 goroutine 不会被阻止退出。超时和取消的正常方法是使用Context
  • 如何使用上下文来解决这个问题?你能分享一个代码sn-p
  • 文档中有示例:WithCancelWithTimeout,此处还有一个帖子Go Concurrency Patterns: Context。您有什么具体问题?
  • context pkg 在 go 标准库中大量使用,用于任何网络/阻塞调用。所以你也应该使用它。它们与级联效果很好地链接 - 从而避免较低级别的代码知道如何实现更高级别的任务。
  • 谁能把上面的代码sn-p修改成context如何解决问题?

标签: go goroutine


【解决方案1】:

这是您的 sn-p 上下文:https://play.golang.org/p/0TXyt4vuGKJ

package main

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

// WaitGroup used by main to wait for parent goroutine
var wg sync.WaitGroup

// Long duration process time
var duration = 100

func main() {
    fmt.Println("In main function - ", runtime.NumGoroutine())
    wg.Add(1)
    ctx, cancel := context.WithCancel(context.Background())
    go parentRoutine(ctx)
    wg.Wait()
    cancel()
    time.Sleep(time.Second) //If main immediately exists the child goroutine does not
                            //have the time to terminate.
    fmt.Println("Completed parent - ", runtime.NumGoroutine())
}

func parentRoutine(ctx context.Context) {
    fmt.Println("Starting parent function - ", runtime.NumGoroutine())
    childRes := make(chan int)

    // Spawning child goroutine
    go func(ctx context.Context) {

        // Here the child is a simulation of a long running process which might take more time than expected timeout. It runs even after parent returns due to timeout

        fmt.Println("Starting child function - ", runtime.NumGoroutine())
        select {
        case <-ctx.Done():
            fmt.Println("Child's context expired - ", runtime.NumGoroutine())
        case <-time.After(time.Duration(duration) * time.Second):
            //time consuming task
        }
        fmt.Println("Child ended - ", runtime.NumGoroutine())
        childRes <- 1
    }(ctx)

    select {
    case <-ctx.Done():
        fmt.Println("Parent's context expired - ", runtime.NumGoroutine())
    case <-childRes:
        fmt.Println("Child completed - ", runtime.NumGoroutine())
    case <-time.After(time.Duration(3) * time.Second):
        fmt.Println("Child timed out - ", runtime.NumGoroutine())
    }
    wg.Done()
}

现在的输出如下:

In main function -  1
Starting parent function -  2
Starting child function -  3
Child timed out -  3
Child's context expired -  2
Child ended -  2
Completed parent -  2

【讨论】:

    猜你喜欢
    • 2018-02-19
    • 1970-01-01
    • 2012-03-02
    • 2018-08-02
    • 1970-01-01
    • 2018-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多