2015-12-10 8 views
5

ウェイトグループのクラッシュを正常に処理する方法は?waitgroupから呼び出された関数からランタイムエラーを検出するにはどうすればよいですか?

つまり、以下のコードスニペットでは、ゴーリンのパニック/クラッシュをキャッチして、メソッド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.") 
} 

答えて

3

まず、あなたが最後にそれを行うことから、それも到達できないので、関数の最初の行でなければなりません回復する繰延機能を登録するためのライン/コードdeferすでにパニックとその前に遅延機能が登録されず、これにより、状態が復元されます。

だからこれにあなたのdo()機能を変更します。

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

第二:あなたは一度だけmain()仕上げ実行します繰延機能にwg.Defer()を呼ぶとして単独でこれは、あなたのコードの仕事をすることはありません - ためになることはありませんmain()wg.Wait()と電話してください。したがってwg.Wait()wg.Done()コールを待ちますが、wg.Done()コールはwg.Wait()リターンまで実行されません。それはデッドロックです。あなたは繰延機能で、do()関数からwg.Done()を呼び出す必要があり

、このような何か:

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

出力(Go Playground上でそれを試してみてください):

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

このコースのために必要変数wgをグローバルスコープに移動します。別のオプションは、引数としてdo()に渡すことです。このようにする場合は、WaitGroupへのポインタを渡さなければならないことに注意してください。それ以外の場合は、コピーのみが渡されます(WaitGroupstructタイプ)、コピーにWaitGroup.Done()を呼び出しても元のものには影響しません。 WaitGroupdo()に渡すことで

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

出力は同じです。この変形はGo Playgroundでお試しください。

0

@iczaは私がWaitGroupシンプルさが好きで、適切WaitGroupとその機能WaitDone

を使用する方法を説明する素晴らしい仕事をしてくれました。しかし、私は、並列ロジックがビジネスロジックと混在することを意味するので、ゴルーチンへの参照を渡す必要があるのは嫌です。

だから私は私のために、この問題を解決するために、この汎用的な機能を思い付いた:

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

だからあなたの例では、このように解決することができます:あなたはそれを使用したい場合

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

を、ここで見つけることができますhttps://github.com/shomali11/util

関連する問題