dsdfd2322 2019-05-28 19:42
浏览 62
已采纳

这段具有非缓冲通道的代码是否会在Go中导致goroutine泄漏?

I'm writing some golang concurrency codes with goroutines and channels, suspecting that my code may cause goroutine leaks. My situation is similar to the following code, or you can open this go playground link.

func main() {
    numCount := 3
    numChan := make(chan int)

    for i := 0; i < numCount; i++ {
        go func(num int) {
            fmt.Printf("Adding num: %d to chan
", num)
            numChan <- num
            fmt.Printf("Adding num: %d to chan Done
", num)
        }(i)
    }

    time.Sleep(time.Second)
    panic("Goroutine Resource Leak Test")
} 

In my opinion, when the main goroutine returns, three goroutines are blocked sending to the unbuffered channel and there will be goroutine leak. This post goroutine leak with buffered channel in Go also suggests that So only if the channel was unbuffered the leak would occur.

The Go Programming Language suggests that:

There’s a handy trick we can use during testing: if instead of returning from main in the event of cancellation, we execute a call to panic, then the runtime will dump the stack of every goroutine in the program. If the main goroutine is the only one left, then it has cleaned up after itself. But if other goroutines remain, they may not have been properly canceled, or perhaps they have been canceled but the cancellation takes time; a little investigation may be worthwhile. The panic dump often contains sufficient information to distinguish these cases.

Therefore, I added panic("Goroutine Resource Leak Test") to the end of the main function to verify my assumption. However, the panic dump only contains main goroutine, that is, there is no resource leak.

Adding num: 0 to chan
Adding num: 1 to chan
Adding num: 2 to chan
panic: Goroutine Resource Leak Test

goroutine 1 [running]:
main.main()
    /tmp/sandbox011109649/prog.go:21 +0xc0

Can someone help explain

  • why there is no goroutine leak, or
  • how should I get the correct panic dump if there is leak

Any suggestion will be appreciated, thanks in advance!

  • 写回答

1条回答 默认 最新

  • dongping1922 2019-05-28 20:01
    关注

    The problem with your code is twofold.

    First, there is, theoretically, a goroutine leak since any attempt to send a value to a channel with zero capacity (an unbuffered channel or a filled up buffered channel) blocks the sending goroutine until a receive operation is done on that channel.

    So, yes, by definition of how channels work, all your three goroutines will be blocked in the numChan <- num statement.

    Second, since some revision of Go, an unhandled panic by default dumps only the stack trace of the panicking goroutine. If you wish to dump the stacks of all the active goroutines, you'd have to tweak the runtime — from the documentation of the package runtime:

    The GOTRACEBACK variable controls the amount of output generated when a Go program fails due to an unrecovered panic or an unexpected runtime condition. By default, a failure prints a stack trace for the current goroutine, eliding functions internal to the run-time system, and then exits with exit code 2. The failure prints stack traces for all goroutines if there is no current goroutine or the failure is internal to the run-time. GOTRACEBACK=none omits the goroutine stack traces entirely. GOTRACEBACK=single (the default) behaves as described above. GOTRACEBACK=all adds stack traces for all user-created goroutines. GOTRACEBACK=system is like “all” but adds stack frames for run-time functions and shows goroutines created internally by the run-time. GOTRACEBACK=crash is like “system” but crashes in an operating system-specific manner instead of exiting. For example, on Unix systems, the crash raises SIGABRT to trigger a core dump. For historical reasons, the GOTRACEBACK settings 0, 1, and 2 are synonyms for none, all, and system, respectively. The runtime/debug package's SetTraceback function allows increasing the amount of output at run time, but it cannot reduce the amount below that specified by the environment variable. See https://golang.org/pkg/runtime/debug/#SetTraceback.


    Also note that you must not ever use timers for (simulating) synchronization: in a toy example this might work but in real life nothing prevents your three goroutines from not having a chance to be run during the time span your main goroutine spent in the call to time.Sleep — so the outcome might be that any number of spawned goroutines had run: from 0 to 3.

    Add there the fact that when main exits the runtime merely kills all the outstanding active goroutines, and the result of the test might be surprising at best.

    Hence a proper solution would be to

    • Merely print the stacks where needed,
    • Make sure you synchronize the sends by the matching receives:
    package main
    
    import (
        "fmt"
        "log"
        "runtime"
    )
    
    func dumpStacks() {
        buf := make([]byte, 32 * 1024)
        n := runtime.Stack(buf, true)
        log.Println(string(buf[:n]))
    }
    
    func main() {
        numCount := 3
        numChan := make(chan int, numCount)
    
        for i := 0; i < numCount; i++ {
            go func(num int) {
                fmt.Printf("Adding num: %d to chan
    ", num)
                numChan <- num
                fmt.Printf("Adding num: %d to chan Done
    ", num)
            }(i)
        }
    
        dumpStacks()
    
        for i := 0; i < numCount; i++ {
            <-numChan
        }
    }
    

    Playground.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 基于卷积神经网络的声纹识别
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图