将密码恢复传递给上层的 goroutine(Go 语言)

huangapple go评论87阅读模式
英文:

pass recovery to upper goroutine golang

问题

我创建了一个示例,在其中并发运行函数,函数内部发生 panic 和 recover:

package main

import "fmt"

func main() {
    // "main" recovery
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("main goroutine panicked:", r)
        }
    }()

    // 在其中并发运行函数,函数内部发生 panic
    chanStr := make(chan string)
    go func() {
        // "internal" goroutine recovery
        defer func() {
            if r := recover(); r != nil {
                fmt.Println("internal goroutine panicked:", r)
            }
            chanStr <- "hello world"
        }()
        // 在 "internal" recovery 和 "main" recovery 中都发生 panic,并希望进行恢复
        panic("NOT main goroutine")
    }()
    // 等待带有 "internal" goroutine 的 chan 发生 panic 和恢复
    str := <-chanStr
    fmt.Println(str)

    // panic("main")
}

它的输出是:

internal goroutine panicked: NOT main goroutine
hello world

是否有可能修改我的代码,使恢复从 "internal" 传递到 "main"?换句话说,我希望它在控制台上输出:

internal goroutine panicked: NOT main goroutine
main goroutine panicked: main
hello world

我尝试通过完全删除 "internal" recovery 函数来实现这一点,但在这种情况下,"main" recovery 无法恢复 "internal" goroutine 中的 panic。

Playground

更新

我尝试按照 @Momer 的建议,通过通道发送错误并在主 goroutine 中处理它,而不是试图将 panic 向上传递:

package main

import (
    "errors"
    "fmt"
)

func main() {
    // "main" recovery
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("main goroutine panicked:", r)
        }
    }()

    // 在其中并发运行函数,函数内部发生 panic
    chanStr := make(chan string)
    chanErr := make(chan error)
    var err error
    go func() {
        // "internal" goroutine recovery
        defer func() {
            if r := recover(); r != nil {
                fmt.Println("internal goroutine panicked:", r)
                switch t := r.(type) {
                case string:
                    fmt.Println("err is string")
                    err = errors.New(t)
                case error:
                    fmt.Println("err is error")
                    err = t
                default:
                    fmt.Println("err is unknown")
                    err = errors.New("Unknown error")
                }

                chanErr <- err
                chanStr <- ""
            }
        }()
        // 在 "internal" recovery 和 "main" recovery 中都发生 panic,并希望进行恢复
        panic("NOT main goroutine")
        chanStr <- "hello world"
        chanErr <- nil

    }()
    // 等待带有 "internal" goroutine 的 chan 发生 panic 和恢复
    str := <-chanStr
    err = <-chanErr
    fmt.Println(str)
    fmt.Println(err)

    // panic("main")
}

它给出了错误:

all goroutines are asleep - deadlock

完整输出:

go run /goPath/parentRecoverty2.go
internal goroutine panicked: NOT main goroutine
err is string
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /goPath/parentRecoverty2.go:48 +0x1d4

goroutine 5 [chan send]:
main.func·002()
    /goPath/parentRecoverty2.go:37 +0x407
main.func·003()
    /goPath/parentRecoverty2.go:42 +0x130
created by main.main
    /goPath/parentRecoverty2.go:46 +0x190
exit status 2

更新的 Playground

英文:

I created an example where I run function concurrently inside which I panic and recover:

package main

import &quot;fmt&quot;

func main() {
    // &quot;main&quot; recovery
    defer func() {
        if r := recover(); r != nil {
            fmt.Println(&quot;main goroutine paniced:&quot;, r)
        }
    }()

    // running function concurrently inside which I panic
    chanStr := make(chan string)
    go func() {
        // this &quot;internal&quot; goroutin recovery
        defer func() {
            if r := recover(); r != nil {
                fmt.Println(&quot;internal goroutine paniced:&quot;, r)
            }
            chanStr &lt;- &quot;hello world&quot;
        }()
        // panicking and wanting recovery not only in &quot;internal&quot; recovery but in &quot;main&quot; recovery as well
        panic(&quot;NOT main goroutine&quot;)
    }()
    // waiting for chan with &quot;internal&quot; goroutine panicking and recovery
    str := &lt;-chanStr
    fmt.Println(str)

    // panic(&quot;main&quot;)
}

It gives output:

internal goroutine panicked: NOT main goroutine
hello world

Is it possible to change my code to make pass recovery from "internal" to "main"? In other words I want it to write down to console:

internal goroutine paniced: NOT main goroutine
main goroutine paniced: main
hello world

I tried to implement this by removing "internal" recovery func at all, but "main" recovery do not recover panic inside "internal" goroutine in this case.

Playground

Update

I tried to follow @Momer's advice and send an error through the channel and handle it in the main goroutine, instead of trying to bubble the panic up:

package main

import (
    &quot;errors&quot;
    &quot;fmt&quot;
)

func main() {
    // &quot;main&quot; recovery
    defer func() {
        if r := recover(); r != nil {
            fmt.Println(&quot;main goroutine paniced:&quot;, r)
        }
    }()

    // running func concarantly inside which I panic
    chanStr := make(chan string)
    chanErr := make(chan error)
    var err error
    go func() {
        // this &quot;internal&quot; goroutin recovery
        defer func() {
            if r := recover(); r != nil {
                fmt.Println(&quot;internal goroutine paniced:&quot;, r)
                switch t := r.(type) {
                case string:
                    fmt.Println(&quot;err is string&quot;)
                    err = errors.New(t)
                case error:
                    fmt.Println(&quot;err is error&quot;)
                    err = t
                default:
                    fmt.Println(&quot;err is unknown&quot;)
                    err = errors.New(&quot;Unknown error&quot;)
                }

                chanErr &lt;- err
                chanStr &lt;- &quot;&quot;
            }
        }()
        // panicing and wanting recovery not only in &quot;internal&quot; recovery but in &quot;main&quot; recovery as well
        panic(&quot;NOT main goroutine&quot;)
        chanStr &lt;- &quot;hello world&quot;
        chanErr &lt;- nil

    }()
    // waiting for chan with &quot;internal&quot; goroutin panicing and recovery
    str := &lt;-chanStr
    err = &lt;-chanErr
    fmt.Println(str)
    fmt.Println(err)

    // panic(&quot;main&quot;)
}

It gives error

all goroutines are asleep - deadlock

Full output:

go run /goPath/parentRecoverty2.go
internal goroutine paniced: NOT main goroutine
err is string
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /goPath/parentRecoverty2.go:48 +0x1d4

goroutine 5 [chan send]:
main.func&#183;002()
    /goPath/parentRecoverty2.go:37 +0x407
main.func&#183;003()
    /goPath/parentRecoverty2.go:42 +0x130
created by main.main
    /goPath/parentRecoverty2.go:46 +0x190
exit status 2

Update playground

答案1

得分: 2

我在Go语言中使用panic/recover,类似于Java或C++中的try/catch/final块。

更多详细信息,请访问Handling panics (from Golang spec)

因此,你可以将panic传递给方法的调用者。下面是一个简单的代码示例,希望对你有所帮助:

注意:在函数Foo()中,我使用recover()来捕获出现问题的情况,然后重新引发panic,以便在外部调用者中捕获它。

package main

import (
	"fmt"
	"time"
)

func Foo() {
	defer func() {
		if x := recover(); x != nil {
			fmt.Printf("Runtime panic: %v \n", x)
			panic("Ah oh ... Panic in defer")
		}
	}()
	panic("Panic in Foo() !")
}

func Game() {
	defer func(){
		fmt.Println("Clean up in Game()")
	}()

	defer func() {
		if x := recover(); x != nil {
			fmt.Println("Catch recover panic !!! In Game()")
		}
	}()
	Foo()
}

func main() {

	defer func() {
		fmt.Println("Program Quit ... ")
	}()

	fmt.Println("-----------Split-------------")
	go Game()
	time.Sleep(1 * time.Millisecond)
	fmt.Println("-----------Split-------------")
}

希望对你有所帮助!

英文:

I took panic/recover in golang as try/catch/final blocks in java or c++.

For more detail, you can visit Handling panics (from Golang spec).

so you can pass a panic to method's caller.
a simple code is below, hope it helps

Note: in function Foo(), I use recover() to catch things going wrong, and then re-panic in order to catch it later in outer caller.

package main

import (
    &quot;fmt&quot;
    &quot;time&quot;
)

func Foo() {
    defer func() {
	    if x := recover(); x != nil {
		    fmt.Printf(&quot;Runtime panic: %v \n&quot;, x)
		    panic(&quot;Ah oh ... Panic in defer&quot;)
	    }
    }()
    panic(&quot;Panic in Foo() !&quot;)
}

func Game() {
    defer func(){
	    fmt.Println(&quot;Clean up in Game()&quot;)
    }()

    defer func() {
	    if x := recover(); x != nil {
		    fmt.Println(&quot;Catch recover panic !!! In Game()&quot;)
	    }
    }()
    Foo()

}

func main() {

    defer func() {
	    fmt.Println(&quot;Program Quit ... &quot;)
    }()

    fmt.Println(&quot;-----------Split-------------&quot;)
    go Game()
    time.Sleep(1 * time.Millisecond)
    fmt.Println(&quot;-----------Split-------------&quot;)
}

答案2

得分: 1

在你更新的问题中,一个线程被阻塞在从chanStr读取,而另一个线程被阻塞在向chanErr写入。
交换写入的顺序应该可以解决死锁问题。

defer func() {
    if r := recover(); r != nil {
        fmt.Println("内部协程发生恐慌:", r)
        switch t := r.(type) {
        case string:
            fmt.Println("错误是字符串")
            err = errors.New(t)
        case error:
            fmt.Println("错误是错误类型")
            err = t
        default:
            fmt.Println("错误是未知类型")
            err = errors.New("未知错误")
        }
        chanStr <- ""
        chanErr <- err
    }
}
英文:

In your updated question, one thread is blocked by reading from chanStr while the other thread is blocked by writing into chanErr.
Switching the order of writes should resolve the deadlock.

defer func() {
    if r := recover(); r != nil {
        fmt.Println(&quot;internal goroutine paniced:&quot;, r)
        switch t := r.(type) {
        case string:
            fmt.Println(&quot;err is string&quot;)
            err = errors.New(t)
        case error:
            fmt.Println(&quot;err is error&quot;)
            err = t
        default:
            fmt.Println(&quot;err is unknown&quot;)
            err = errors.New(&quot;Unknown error&quot;)
        }
        chanStr &lt;- &quot;&quot;
        chanErr &lt;- err
    }
}

huangapple
  • 本文由 发表于 2015年4月5日 21:36:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/29457856.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定