Golang,重启恐慌的例程的正确方法

时间:2017-01-12 00:37:24

标签: go

我有以下示例代码。我想保持4个goroutines一直在运行。他们有恐慌的可能。在恐慌的情况下,我有一个恢复,我重新启动goroutine。

我实施的方式有效,但我不确定它是否是正确和正确的方法。任何想法

package main

import (
    "fmt"
    "time"
)

var gVar string
var pCount int

func pinger(c chan int) {
    for i := 0; ; i++ {
        fmt.Println("adding ", i)
        c <- i
    }
}

func printer(id int, c chan int) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("HERE", id)
            fmt.Println(err)
            pCount++
            if pCount == 5 {
                panic("TOO MANY PANICS")
            } else {
                go printer(id, c)
            }
        }
    }()

    for {
        msg := <-c
        fmt.Println(id, "- ping", msg, gVar)
        if msg%5 == 0 {
            panic("PANIC")
        }

        time.Sleep(time.Second * 1)

    }
}

func main() {
    var c chan int = make(chan int, 2)
    gVar = "Preflight"
    pCount = 0

    go pinger(c)
    go printer(1, c)
    go printer(2, c)
    go printer(3, c)
    go printer(4, c)

    var input string
    fmt.Scanln(&input)
}

4 个答案:

答案 0 :(得分:8)

您可以在以下函数中提取恢复逻辑:

func recoverer(maxPanics, id int, f func()) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("HERE", id)
            fmt.Println(err)
            if maxPanics == 0 {
                panic("TOO MANY PANICS")
            } else {
                go recoverer(maxPanics-1, id, f)
            }
        }
    }()
    f()
}

然后使用它:

go recoverer(5, 1, func() { printer(1, c) })

答案 1 :(得分:1)

哦,我不是说以下比你的方式更正确。这只是另一种方式。

创建另一个函数,将其称为printerRecover或类似的函数,并在那里执行延迟/恢复。然后在printer中循环调用printerRecover。添加函数返回值以检查是否由于某种原因需要goroutine退出。

答案 2 :(得分:1)

就像Zan Lynx的答案一样,我想分享另一种方法(虽然它与OP的方式非常相似。)我使用了额外的缓冲通道ch。当goroutine发生恐慌时,goroutine内部的恢复功能会将其身份i发送给ch。在main()底部的for循环中,它会通过接收来自ch的值来检测恐慌中的哪个goroutine以及是否重新启动。

Run in Go Playground

package main

import (
    "fmt"
    "time"
)

func main() {
    var pCount int
    ch := make(chan int, 5)

    f := func(i int) {
        defer func() {
            if err := recover(); err != nil {
                ch <- i
            }
        }()

        fmt.Printf("goroutine f(%v) started\n", i)
        time.Sleep(1000 * time.Millisecond)
        panic("goroutine in panic")
    }

    go f(1)
    go f(2)
    go f(3)
    go f(4)

    for {
        i := <-ch
        pCount++
        if pCount >= 5 {
            fmt.Println("Too many panics")
            break
        }
        fmt.Printf("Detected goroutine f(%v) panic, will restart\n", i)
        f(i)
    }
}

答案 3 :(得分:0)

您实施的方式是正确的。对我来说,保持正好运行4个例程的方法看起来并没有多少go_way,要么处理例程的ID,要么延迟产生,这可能导致由于关闭导致不可预测的堆栈。我认为你不能以这种方式有效地平衡资源。你需要什么时候不喜欢简单的产卵工人

func main() {
...
    go func(tasks chan int){ //multiplexer
        for {
            task = <-tasks  //when needed
            go printer(task) //just spawns handler
        }
    }(ch)
...
}

让运行时完成它的工作?这样就可以在stdlib监听器/服务器中完成这些操作,并且已知它们足够高效。 goroutines非常轻量级,并且运行时非常智能以平衡负载。当然你必须以任何一种方式恢复。这是我个人的看法。