2015-12-10 8 views
5

Jak sprawnie obsługiwać miażdżenia w grupie roboczej?Jak wychwycić błąd środowiska wykonawczego z funkcji wywoływanej z grupy wait?

Innymi słowy, w poniższym fragmencie kodu, jak złapać panikę/miażdżenie gorourines wywołując metodę do()?

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.") 
} 

Odpowiedz

3

Najpierw rejestracji funkcję odroczony do odzyskania powinna być w pierwszej linii w funkcji, jak to zrobić, ponieważ ostatni, nie będzie nawet być osiągnięte, ponieważ linia/code przed defer już paniki i tak funkcja odroczona nie zostanie zarejestrowana, co przywróci stan panowania.

tak zmienić funkcję do() do tego:

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

Po drugie: to sam nie zrobi swoją pracę kodu, jak to nazywasz wg.Defer() w funkcji odroczony które uruchamiane tylko raz main() wykończeń - który nigdy się tak dlatego, dzwonisz pod numer wg.Wait() w swoim main(). Tak więc wg.Wait() czeka na wywołania wg.Done(), ale połączenia wg.Done() nie zostaną uruchomione, dopóki nie zostaną przywrócone po wg.Wait(). To jest impas.

Należy zadzwonić wg.Done() z funkcji do() w funkcji odroczonym coś takiego:

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 (spróbuj go na Go Playground):

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

To oczywiście potrzebne do przenieś zmienną wg do zasięgu globalnego. Inną opcją byłoby przekazanie jej jako argumentu do do(). Jeśli zdecydujesz się pójść tą drogą, pamiętaj, że musisz przekazać wskaźnik do WaitGroup, w przeciwnym razie zostanie przekazana tylko kopia (WaitGroup to typ struct), a wywołanie WaitGroup.Done() na kopii nie będzie miało wpływu na oryginał.

Przy przejściu WaitGroup do 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.") 
} 

wyjściowa jest taka sama. Wypróbuj ten wariant na Go Playground.

0

@icza fantastyczną robotę wyjaśniający jak odpowiednio wykorzystać WaitGroup i jego funkcje Wait i Done

Lubię WaitGroup prostotę. Nie podoba mi się jednak, że musimy przekazać odniesienie do goroutine, ponieważ oznaczałoby to, że logika współbieżności byłaby mieszana z logiką biznesową.

Więc wymyśliłem ten rodzajowy funkcji, aby rozwiązać ten problem dla mnie:

// 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) 
    } 
} 

Więc Przykładem może być rozwiązany w ten sposób:

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.") 
} 

Jeśli chcesz go używać, możesz go znaleźć tutaj https://github.com/shomali11/util