如何等待恐慌的goroutine?

A common way to wait for a goroutine is to use a *sync.WaitGroup:

func main() {
    wg := &sync.WaitGroup{}
    wg.Add(1)
    go func() {
        defer wg.Done()
        // Long running task
    }()
    wg.Wait()
}

No problems here. However, what about this:

func main() {
    wg := &sync.WaitGroup{}
    wg.Add(1)
    go func() {
        defer wg.Done()
        // Long running task
        panic("Something unexpected happened.")
    }()
    wg.Wait()
}

In this case, when wg.Done() is called, I believe main() could exit without details of panic() ever being written to stdout/stderr. Is this true and if yes, how might I prevent it from happening?

The panic will kill the process regardless, because no one is recovering from it. If you want to recover from panics in a goroutine, you must have recover wrapping the call stack in the same goroutine.

wg.Done will be called in this case, by the defer statement. But the process may die before the main goroutine finishes the wg.Wait anyway.

@Eli Bendersky was right.

Reference src/builtin/builtin.go

The panic built-in function stops normal execution of the current goroutine. When a function F calls panic, normal execution of F stops immediately. Any functions whose execution was deferred by F are run in the usual way, and then F returns to its caller. To the caller G, the invocation of F then behaves like a call to panic, terminating G's execution and running any deferred functions. This continues until all functions in the executing goroutine have stopped, in reverse order. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking and can be controlled by the built-in function recover.

after panic, defer func will be called.

Check this in playground: https://play.golang.org/p/yrXkEbE1Af7

package main

import (
    "sync"
    "fmt"
)

func main() {
    wg := &sync.WaitGroup{}
    wg.Add(1)
    go func() {
        defer func(){
            fmt.Println("expected to be called after panic")
            wg.Done()
        }()
        // Long running task
        panic("Something unexpected happened.")
    }()
    wg.Wait()
}

Output

expected to be called after panic
panic: Something unexpected happened.

goroutine 5 [running]:
main.main.func1(0x416020, 0x0)
    /tmp/sandbox946785562/main.go:17 +0x60
created by main.main
    /tmp/sandbox946785562/main.go:11 +0x80

Then your second question, "how to prevent that?"

As metioned before, you can recover after panic

Playground: https://play.golang.org/p/76pPrCVYN8u

package main

import (
    "sync"
    "fmt"
)

func main() {
    wg := &sync.WaitGroup{}
    wg.Add(1)
    go func() {
        defer func(){
            if x:=recover();x!=nil{
                fmt.Printf("%+v
",x)
            }
            wg.Done()
        }()
        // Long running task
        panic("Something unexpected happened.")
    }()
    wg.Wait()
    for i:=0;i<10;i++{
        fmt.Println(i)
    }
}

Output

Something unexpected happened.
0
1
2
3
4
5
6
7
8
9