【问题标题】:Why happen here a deadlock为什么这里会发生死锁
【发布时间】:2014-07-23 09:19:02
【问题描述】:

我想了解 golang 频道的工作原理。我读了一本关于 go 语言的书,发现了下面的例子。

package main

import (
    "fmt"
)

// Send the sequence 2, 3, 4, ... to returned channel 
func generate() chan int {
    ch := make(chan int)
    go func() {
        for i := 2; i <= 100 ; i++ {
            ch <- i
        }
    }()
    return ch
}

// Filter out input values divisible by 'prime', send rest to returned channel
func filter(in chan int, prime int) chan int {
    out := make(chan int)
    go func() {
        for {
            if i := <-in; i%prime != 0 {
                out <- i
            }
        }
    }()
    return out
}

func sieve() chan int {
    out := make(chan int)
    go func() {
        ch := generate()
        for {
            prime := <-ch
            ch = filter(ch, prime)
            out <- prime
        }
    }()
    return out
}

func main() {
    primes := sieve()
    for {
        fmt.Println(<-primes)
    }
}

当我运行这个程序时,我遇到了死锁,但是当我将生成函数更改为

// Send the sequence 2, 3, 4, ... to returned channel 
func generate() chan int {
    ch := make(chan int)
    go func() {
        for i := 2; ; i++ {
            ch <- i
        }
    }()
    return ch
}

然后程序将运行无限循环,但不会死锁。当我删除 for 循环中的条件时,为什么会出现死锁?

【问题讨论】:

    标签: go


    【解决方案1】:

    阻塞原则是什么意思?

    您可以在博文“The Nature Of Channels In Go ”中看到它的说明

    对于无缓冲通道:

    (插图来自博文“The Nature Of Channels In Go ”,作者 William Kennedy,2014 年 2 月)

    无缓冲通道没有容量,因此需要两个 goroutine 准备好进行任何交换
    当 goroutine 尝试将资源写入无缓冲通道并且没有 goroutine 等待接收资源时,通道将锁定 goroutine 并使其等待。
    当 goroutine 尝试从无缓冲通道读取时,并且没有 goroutine 等待发送资源,channel 将锁定 goroutine 并使其等待

    这就是你的读者的情况:

    func main() {
        primes := sieve()
        for {
            fmt.Println(<-primes)
        }
    }
    

    由于primes 从未关闭,main 仍然被阻止。
    它 (main) 在第 3 步:

    在第 3 步中,右侧的 goroutine 将手伸入通道或执行 读取
    该 goroutine 也被锁定在通道中,直到交换完成。

    发件人从不打电话给close(primes)

    【讨论】:

    • 我死锁了,因为函数 generate 中的 goroutine 是通道在 100 次循环后关闭并且素数仍在等待接收数据对吗?
    • @zero_coding 它没有关闭:我在任何地方都看不到close(primes)
    • 我的意思是因为接收者没有关闭,但发送者关闭了对吧?没有更多的发件人了。
    • 不,发件人永远不会关闭它
    • 现在我明白了。非常感谢。
    【解决方案2】:

    让我们考虑一个更简单的例子:

    func generate() chan int {
        ch := make(chan int)
        go func() {
            for i := 2; /*i < 100*/; i++ {
                ch <- i
            }
        }()
        return ch
    }
    
    func main() {
        for i := range generate() {
            fmt.Println(i)
        }
    }
    

    i &lt; 100 未注释条件,generate 生成的 goroutine 在发送 98 个数字后停止。但是,它并没有关闭通道,所以main 无法知道不会再发送更多号码,它只是一直阻塞在通道上。由于 main 现在是唯一仍然存在的 goroutine(另一个已经返回),并且它正在阻塞,因此您遇到了死锁。

    【讨论】:

    • 我也读过关于频道阻塞的话题,但我不明白,阻塞频道是什么意思。通道由发送者和接收者组成。发件人一次只能发送一次,这意味着收件人必须拿到包裹,直到它可以继续他的工作。阻塞原则是什么意思?
    猜你喜欢
    • 2019-12-16
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-27
    • 2021-12-10
    相关资源
    最近更新 更多