简体   繁体   English

如何等待一个恐慌的 goroutine?

[英]How to wait for a panicking goroutine?

A common way to wait for a goroutine is to use a *sync.WaitGroup :等待 goroutine 的常用方法是使用*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 .在这种情况下,当wg.Done()被调用时,我相信main()可以退出,而不会将panic()细节写入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.无论如何, panic都会杀死这个过程,因为没有人能从中恢复。 If you want to recover from panics in a goroutine, you must have recover wrapping the call stack in the same goroutine.如果你想从一个够程恐慌中恢复过来,你必须recover包裹在同一个够程调用堆栈。

wg.Done will be called in this case, by the defer statement.在这种情况下,将通过defer语句调用wg.Done But the process may die before the main goroutine finishes the wg.Wait anyway.但是进程可能会在主 goroutine 完成wg.Wait之前wg.Wait

@Eli Bendersky was right. @Eli Bendersky 是对的。

Reference src/builtin/builtin.go参考src/builtin/builtin.go

The panic built-in function stops normal execution of the current goroutine. panic 内置函数停止当前 goroutine 的正常执行。 When a function F calls panic, normal execution of F stops immediately.当函数 F 调用 panic 时,F 的正常执行会立即停止。 Any functions whose execution was deferred by F are run in the usual way, and then F returns to its caller.任何被 F 推迟执行的函数都以通常的方式运行,然后 F 返回给它的调用者。 To the caller G, the invocation of F then behaves like a call to panic, terminating G's execution and running any deferred functions.对于调用者 G 来说,F 的调用就像调用 panic,终止 G 的执行并运行任何延迟函数。 This continues until all functions in the executing goroutine have stopped, in reverse order.这一直持续到正在执行的 goroutine 中的所有函数都以相反的顺序停止。 At that point, the program is terminated and the error condition is reported, including the value of the argument to panic.此时,程序终止并报告错误条件,包括 panic 的参数值。 This termination sequence is called panicking and can be controlled by the built-in function recover.这种终止序列称为恐慌,可以由内置函数恢复控制。

after panic , defer func will be called.panic之后, defer func 将被调用。

Check this in playground: https://play.golang.org/p/yrXkEbE1Af7在操场上检查这个: 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如前所述,您可以在panicrecover

Playground: https://play.golang.org/p/76pPrCVYN8u游乐场: 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\n",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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM