【问题标题】:All goroutines are asleep所有的 goroutine 都处于休眠状态
【发布时间】:2018-11-06 10:01:34
【问题描述】:

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

    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!。但是我无法解决问题,我不知道出了什么问题。有人可以帮忙吗?

【问题讨论】:

  • wg 声明在哪里?
  • 抱歉,我忘记了。 wg 的类型是 sync.WaitGroup。 @AkhilThayyil
  • 如果您这样做是为了学习,那很好,但作为替代方案,您可能需要考虑为此目的使用原子计数器,可以在此处找到示例gobyexample.com/atomic-counters
  • 谢谢。我会试一试。@tmcnicol

标签: go


【解决方案1】:

频道make(chan int) 的隐式大小为零(参考:https://golang.org/ref/spec#Making_slices_maps_and_channels

大小为零的通道是无缓冲的。一个指定大小的通道 make(chan int, n) 被缓冲。请参阅http://golang.org/ref/spec#Send_statements,了解有关缓冲通道与无缓冲通道的讨论。 http://play.golang.org/p/VZAiN1V8-P 的示例说明了差异。

在这里,频道&lt;-chch &lt;- 将被阻止,直到有人处理它(同时)。如果你用纸笔试试这个程序的流程,你就会明白为什么它被阻止了。下图显示了可能通过通道ch的数据流:

因此,如果您将 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)时分析程序是如何工作的,我们可以看到这个程序中阻塞了一个goroutine(另一个退出了)。借助time.Sleep(n) 并在阻塞的 go 例程中从通道接收最后一个数据,我们可以克服死锁。请看下面的代码:

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

【讨论】:

    猜你喜欢
    • 2013-11-22
    • 2012-09-06
    • 2019-07-26
    • 2021-04-05
    • 1970-01-01
    • 2015-04-02
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    相关资源
    最近更新 更多