2015-12-10 2 views
5

Wie kann Crushes in einer Waitgroup elegant behandelt werden?Wie kann man Laufzeitfehler von einer Funktion, die von einer Waitgroup aufgerufen wird, abfangen?

Mit anderen Worten, im folgenden Codeschnipsel, wie die Panics/Crushes von Gorourines Aufruf Methode do() fangen?

func do(){ 
    str := "abc" 
    fmt.Print(str[3]) 
    defer func() { 
     if err := recover(); err != nil { 
      fmt.Print(err) 
     } 
    }() 
} 

func main() { 
    var wg sync.WaitGroup 

    for i := 0; i < 1; i++ { 
     wg.Add(1) 
     go do() 
     defer func() { 
      wg.Done() 
      if err := recover(); err != nil { 
       fmt.Print(err) 
      } 
     }() 
    } 
    wg.Wait() 
    fmt.Println("This line should be printed after all those invocations fail.") 
} 

Antwort

3

zunächst eine latente Funktion Registrierung die erste Zeile in der Funktion zu erholen sollte, da, da Sie es zuletzt tun, ist es nicht einmal, weil die Leitung/Code erreicht wird, bevor die defer schon Panik und so Die Deferred-Funktion wird nicht registriert, wodurch der Panicing-Status wiederhergestellt würde.

So Ihre do() Funktion dies ändern:

func do() { 
    defer func() { 
     if err := recover(); err != nil { 
      fmt.Println("Restored:", err) 
     } 
    }() 
    str := "abc" 
    fmt.Print(str[3]) 
} 

Zweitens: dies allein wird nicht der Code funktioniert, wie Sie wg.Defer() in einer latenten Funktion aufrufen, die nur main() Oberflächen einmal laufen würde - was nie da ist Sie rufen wg.Wait() in Ihrem main(). So wg.Wait() wartet auf die wg.Done() Anrufe, aber wg.Done() Anrufe werden nicht ausgeführt, bis wg.Wait() zurückkehrt. Es ist eine Sackgasse.

Sie wg.Done() aus der do() Funktion aufrufen sollte, in der latenten Funktion, etwa wie folgt:

var wg sync.WaitGroup 

func do() { 
    defer func() { 
     if err := recover(); err != nil { 
      fmt.Println(err) 
     } 
     wg.Done() 
    }() 
    str := "abc" 
    fmt.Print(str[3]) 
} 

func main() { 
    for i := 0; i < 1; i++ { 
     wg.Add(1) 
     go do() 
    } 
    wg.Wait() 
    fmt.Println("This line should be printed after all those invocations fail.") 
} 

Output (versuchen Sie es auf dem Go Playground):

Restored: runtime error: index out of range 
This line should be printed after all those invocations fail. 

Dies ist natürlich notwendig, um Verschieben Sie die Variable wg in den globalen Bereich. Eine andere Möglichkeit wäre, sie als Argument an do() zu übergeben. Wenn Sie sich für diesen Weg entscheiden, beachten Sie, dass Sie einen Zeiger an WaitGroup übergeben müssen, sonst wird nur eine Kopie übergeben (WaitGroup ist ein struct Typ) und der Aufruf WaitGroup.Done() auf einer Kopie wird keine Auswirkungen auf das Original haben.

Mit vorbei WaitGroup-do():

func do(wg *sync.WaitGroup) { 
    defer func() { 
     if err := recover(); err != nil { 
      fmt.Println("Restored:", err) 
     } 
     wg.Done() 
    }() 
    str := "abc" 
    fmt.Print(str[3]) 
} 

func main() { 
    var wg sync.WaitGroup 
    for i := 0; i < 1; i++ { 
     wg.Add(1) 
     go do(&wg) 
    } 
    wg.Wait() 
    fmt.Println("This line should be printed after all those invocations fail.") 
} 

Ausgabe ist das gleiche. Versuchen Sie diese Variante auf der Go Playground.

0

@icza hat einen fantastischen Job zu erklären, wie man angemessen WaitGroup nutzen und seine Funktionen Wait und Done

Ich mag WaitGroup Einfachheit. Ich mag es jedoch nicht, dass wir den Verweis auf die Goroutine weitergeben müssen, weil das bedeuten würde, dass die Logik für den gemeinsamen Zugriff mit Ihrer Geschäftslogik gemischt wäre.

So kam ich mit dieser generischen Funktion nach oben für mich, dieses Problem zu lösen:

// Parallelize parallelizes the function calls 
func Parallelize(functions ...func()) { 
    var waitGroup sync.WaitGroup 
    waitGroup.Add(len(functions)) 

    defer waitGroup.Wait() 

    for _, function := range functions { 
     go func(copy func()) { 
      defer waitGroup.Done() 
      copy() 
     }(function) 
    } 
} 

So könnte Ihr Beispiel auf diese Weise gelöst werden:

func do() { 
    defer func() { 
     if err := recover(); err != nil { 
      fmt.Println(err) 
     } 
    }() 

    str := "abc" 
    fmt.Print(str[3]) 
} 

func main() { 
    Parallelize(do, do, do) 

    fmt.Println("This line should be printed after all those invocations fail.") 
} 

Wenn Sie es verwenden mögen, Sie können es hier finden https://github.com/shomali11/util

Verwandte Themen