我编写了一些旨在使用 channel 进行同步的代码。

    var counter int64  // shared resource

    var wg sync.WaitGroup

    func main() {
        ch := make(chan int64)

        wg.Add(2)

        go incCounter(ch)
        go incCounter(ch)

        ch <- counter

        wg.Wait()
        fmt.Println("Final Counter:", counter) // expected value is 4
    }

    func incCounter(ch chan int64) {
        defer wg.Done()

        for count := 0; count < 2; count++ {
            value := <-ch
            value++
            counter = value
            ch <- counter
        }
    }

当我运行该程序时,发生了一个错误:all goroutines are asleep - deadlock!。但是我无法解决问题,也不知道出了什么问题。有人可以帮忙吗?

最佳答案

channel make(chan int)的隐式大小为零(引用:https://golang.org/ref/spec#Making_slices_maps_and_channels)

大小为零的 channel 未缓冲。指定大小的 channel make(chan int,n)被缓冲。有关缓冲 channel 与非缓冲 channel 的讨论,请参见http://golang.org/ref/spec#Send_statementshttp://play.golang.org/p/VZAiN1V8-P上的示例说明了差异。

在这里, channel <-chch <-将被阻止,直到有人对其进行处理(同时)。如果您尝试用笔和纸写这个程序的流程,您将弄清楚为什么它被阻塞了。下图显示了可能通过 channel ch的数据流:

go - 所有goroutine都在 sleep-LMLPHP

因此,如果将ch := make(chan int64)设置为ch := make(chan int64,1),它将起作用。

var counter int64 // shared resource
var wg sync.WaitGroup

func main() {
    ch := make(chan int64, 1)

    wg.Add(2)

    go incCounter(ch)
    go incCounter(ch)

    ch <- counter

    wg.Wait()
    fmt.Println("Final Counter:", counter) // expected value is 4
}

func incCounter(ch chan int64) {
    defer wg.Done()

    for count := 0; count < 2; count++ {
        value := <-ch
        value++
        counter = value
        ch <- counter
    }
}

如果我们分析使用ch := make(chan int64)时程序的工作方式,我们可以看到此程序中阻塞了一个go例程(退出了另一个例程)。借助time.Sleep(n)并在阻塞的go例程中从 channel 接收最后的数据,我们可以克服死锁。请参见下面的代码:
var counter int64 // shared resource
var wg sync.WaitGroup

func main() {
    ch := make(chan int64)

    wg.Add(2)

    go incCounter(ch)
    go incCounter(ch)

    ch <- counter

    // to ensure one go routine 'incCounter' is completed and one go routine is blocked for unbuffered channel
    time.Sleep(3*time.Second)

    <-ch // to unblock the last go routine

    wg.Wait()
    fmt.Println("Final Counter:", counter) // expected value is 4
}

func incCounter(ch chan int64) {
    defer wg.Done()

    for count := 0; count < 2; count++ {
        value := <-ch
        value++
        counter = value
        ch <- counter
    }
}

关于go - 所有goroutine都在 sleep ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53169598/

10-16 01:45