Pertanyaan Bagaimana cara menangkap error runtime dari suatu fungsi yang dipanggil dari suatu waitgroup?


Bagaimana cara menangani naksir dalam grup waitgroup dengan anggun?

Dengan kata lain, dalam potongan kode berikut, bagaimana cara menangkap panik / meremukkan metode gorourines invoking 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.")
}

5
2017-12-10 07:39


asal


Jawaban:


Pertama, mendaftarkan fungsi yang ditangguhkan untuk memulihkan harus menjadi baris pertama dalam fungsi, karena sejak Anda melakukannya terakhir, itu tidak akan tercapai karena garis / kode sebelum defer sudah panik dan sehingga fungsi yang ditangguhkan tidak terdaftar yang akan mengembalikan keadaan panorata.

Jadi ubahlah do() berfungsi untuk ini:

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

Kedua: ini saja tidak akan membuat kode Anda bekerja, begitu Anda menelepon wg.Defer() dalam fungsi yang ditangguhkan yang hanya akan berjalan satu kali main() selesai - yang tidak pernah karena Anda menelepon wg.Wait() di dalam kamu main(). Begitu wg.Wait() menunggu untuk wg.Done() panggilan, tapi wg.Done() panggilan tidak akan dijalankan hingga wg.Wait() kembali. Ini jalan buntu.

Anda harus menelepon wg.Done() dari do() fungsi, dalam fungsi yang ditangguhkan, sesuatu seperti ini:

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 (coba di Go Playground):

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

Ini tentu saja diperlukan untuk memindahkan wg variabel ke lingkup global. Pilihan lain adalah melewatkannya do() sebagai sebuah argumen. Jika Anda memutuskan untuk pergi dengan cara ini, perhatikan bahwa Anda harus meneruskan pointer ke WaitGroup, kalau tidak, hanya salinan yang akan diteruskan (WaitGroup adalah struct ketik) dan menelepon WaitGroup.Done() pada salinan tidak akan berpengaruh pada aslinya.

Dengan lewat WaitGroup untuk 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.")
}

Outputnya sama. Coba varian ini di Go Playground.


3
2017-12-10 07:48



@icza melakukan pekerjaan fantastis yang menjelaskan cara penggunaan yang tepat WaitGroup dan fungsinya Wait dan Done

saya suka WaitGroup kesederhanaan. Namun, saya tidak suka bahwa kita perlu menyampaikan referensi ke goroutine karena itu berarti bahwa logika konkurensi akan dicampur dengan logika bisnis Anda.

Jadi saya datang dengan fungsi umum ini untuk memecahkan masalah ini untuk saya:

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

Jadi contoh Anda bisa dipecahkan dengan cara ini:

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

Jika Anda ingin menggunakannya, Anda dapat menemukannya di sini https://github.com/shomali11/util


0
2018-06-08 01:25