为什么这里发生了僵局

时间:2014-07-23 09:19:03

标签: go

我试图了解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循环中删除条件时,为什么会出现死锁?

2 个答案:

答案 0 :(得分:9)

  

你对阻止原则的意思是什么?

您可以在博文“The Nature Of Channels In Go

中看到它

表示无缓冲频道:

http://3.bp.blogspot.com/-vnJIWvlbP-E/UwDVICJKB9I/AAAAAAAANX0/T04V_58i8Vs/s1600/Screen+Shot+2014-02-16+at+10.10.54+AM.png

(来自博客文章“The Nature Of Channels In Go ”的插图,由 William Kennedy 撰写,2014年2月)

  

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

这就是你的读者所遇到的情况:

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

由于primes永不关闭,main仍然被屏蔽 它(main)在第3步:

  

在步骤3中,右侧的goroutine将手放入通道或执行读取。   在交换完成之前,goroutine也被锁定在频道中

发件人从不致电close(primes)

答案 1 :(得分:5)

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

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 < 100取消注释,则generate生成的goroutine会在发送98个号码后停止。但是,它不会关闭频道,因此main无法知道将不再发送任何数字,并且它只是在频道上保持阻塞。由于main现在仍然是唯一仍然存在的goroutine(另一个已经返回),并且它正在阻塞,所以你有一个僵局。