猿问

我怎样才能避免死锁

看下面的代码片段。


package main


import (

    "errors"

    "fmt"

    "math/rand"

    "runtime"

    "sync"

    "time"

)


func random(min, max int) int {

    rand.Seed(time.Now().Unix())

    return rand.Intn(max-min) + min

}


func err1(rand int, chErr chan error, wg *sync.WaitGroup) {

    if rand == 1 {

        chErr <- errors.New("Error 1")

    }


    wg.Done()


}


func err2(rand int, chErr chan error, wg *sync.WaitGroup) {

    if rand == 2 {

        chErr <- errors.New("Error 2")

    }

    wg.Done()

}


func err3(rand int, chErr chan error, wg *sync.WaitGroup) {

    if rand == 3 {

        chErr <- errors.New("Error 3")

    }

    wg.Done()

}


func err4(rand int, chErr chan error, wg *sync.WaitGroup) {

    if rand == 3 {

        chErr <- errors.New("Error 4")

    }

    wg.Done()

}


func err5(rand int, chErr chan error, wg *sync.WaitGroup) {

    if rand == 4 {

        chErr <- errors.New("Error 5")

    }

    wg.Done()

}


func main() {


    runtime.GOMAXPROCS(runtime.NumCPU())


    chErr := make(chan error, 1)

    wg := new(sync.WaitGroup)


    //n := random(1, 8)

    n := 3

    fmt.Println(n)


    wg.Add(5)

    go err1(n, chErr, wg)

    go err2(n, chErr, wg)

    go err3(n, chErr, wg)

    go err4(n, chErr, wg)

    go err5(n, chErr, wg)


    fmt.Println("Wait")

    wg.Wait()

    select {

    case err := <-chErr:

        fmt.Println(err)

        close(chErr)

    default:

        fmt.Println("NO error, job done")

    }

}

我怎样才能避免这里的僵局?我可以指定缓冲区长度为 2,但也许它有更优雅的方法来解决问题。


我有意识地在函数 err3 和 err4 上做了 rand == 3 。


拉风的咖菲猫
浏览 176回答 3
3回答

catspeake

通常,不要陷入认为更大的缓冲区可以修复死锁的陷阱。这种方法可能在某些特定情况下有效,但通常并非如此。死锁最好通过了解 goroutine 如何相互依赖来解决。本质上,您必须消除相互依赖的通信循环。非阻塞发送的想法(参见@izca 的回答)是一种有用的技巧,但不是唯一的技巧。有大量关于如何避免死锁/活锁的知识。其中大部分来自奥卡姆在 80 年代和 90 年代流行的日子。有一些来自诸如 Jeremy Martin(无死锁并发系统的设计策略)、Peter Welch(高级范式)等人的特别珍宝。客户端-服务器策略很简单:将您的 Go 例程网络描述为一组通信服务器及其客户端;确保网络图中没有循环 => 消除了死锁。I/o-par 是一种形成 Go-routines 的环和环的方法,这样结构内就不会出现死锁;这就是循环特定情况下是允许的,但行为方式一般无死锁的方式。所以,我的策略是首先减少缓冲区大小,考虑发生了什么,修复死锁。然后,根据基准重新引入缓冲区以提高性能。死锁是由通信图中的循环引起的。打破循环。

犯罪嫌疑人X

您的节目陷入僵局,因为您的频道已满。您的频道大小为 1。然后调用wg.Wait().. 等待调用5 个函数。现在,一旦您到达err3..&nbsp;rand == 3,因此您的频道会传递错误。此时,您的频道已满,您只勾选了 3 个等待组项目。err4使用值 3 .. 调用它也想在您的频道上放置错误。此时,它会阻止 - 因为您的频道已满并且没有从中弹出任何内容。所以你的主要 goroutine 会阻塞,因为你的等待组永远不会完成。修复确实是使您的通道缓冲区更大。这样,当错误试图放置在通道上时 - 它不会阻塞,并且您的等待组有机会勾选它的所有项目。
随时随地看视频慕课网APP

相关分类

Go
我要回答