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