关于go的channel阻塞问题

在看go入门指南时遇到一个问题,下面的程序为什么会产生死锁。

package main

import (
    "fmt"
)

func f1(in chan int) {
    fmt.Println(<-in)
}

func main() {
    out := make(chan int)
    out <- 2
    go f1(out)
}

书中说到:通道的发送/接收操作在对方准备好之前是阻塞的:,按理说,out <- 2等到fmt.Println(<-in)准备好就可以执行了,不应该产生死锁啊,求解
但是,当我调整了一下顺序,就可以了

func f1(in chan int) {
    fmt.Println(<-in)
}

func main() {
    out := make(chan int)
    go f1(out)
    out <- 2
}
// 结果:
2

然后在调整一点

func f1(in chan int) {
    in <- 2
    fmt.Println("in")
}

func main() {
    out := make(chan int)
    <-out
    go f1(out)
}
// 结果依然是死锁

如果此时和上面一样调整一下顺序

func f1(in chan int) {
    in <- 2
    fmt.Println("in")
}

func main() {
    out := make(chan int)
    go f1(out)
    <-out
}
// 结果为 in

我又尝试了一下

func f1(in chan int) {
    in <- 2
    fmt.Println("in")
}

func main() {
    out := make(chan int)
    <-out
    fmt.Println("out")
    go f1(out)
}

此时的结果是死锁,还有一个关键的点是:没有打印出out
我猜:是不是由于在执行<-out时阻塞,而此时只有一个协程,并没有执行go f1(out),所以导致channel的双方并没有准备好,进而产生了死锁。不知道对不对,好纠结???

阅读 6.9k
2 个回答

https://segmentfault.com/q/10...

完全一样的问题……

package main

import (
    "fmt"
)

func f1(in chan int) {
    fmt.Println(<-in)
}

func main() {
    out := make(chan int)
    out <- 2
    go f1(out)
}

有啥好纠结的……

记住一点,单纯的out<-和<-in都会阻塞,只有两个同时进行了,才能继续执行。

这里你看上去有个协程,来执行<-in,但是实际上已经阻塞在out<-2了,协程没办法执行下去……

解决办法就两个,要么在out <- 2之前开一个协程去消费这个channel,

要么就给这个channel加个大小,让他里面默认能存1个数据,这样才不会阻塞,即:make(chan int, 1)

新手上路,请多包涵

Channel communication

Channel communication is the main method of synchronization between goroutines. Each send on a particular channel is matched to a corresponding receive from that channel, usually in a different goroutine.

A send on a channel happens before the corresponding receive from that channel completes.

This program:

var c = make(chan int, 10)
var a string

func f() {

a = "hello, world"
c <- 0

}

func main() {

go f()
<-c
print(a)

}
is guaranteed to print "hello, world". The write to a happens before the send on c, which happens before the corresponding receive on c completes, which happens before the print.

The closing of a channel happens before a receive that returns a zero value because the channel is closed.

In the previous example, replacing c <- 0 with close(c) yields a program with the same guaranteed behavior.

A receive from an unbuffered channel happens before the send on that channel completes.

This program (as above, but with the send and receive statements swapped and using an unbuffered channel):

var c = make(chan int)
var a string

func f() {

a = "hello, world"
<-c

}
func main() {

go f()
c <- 0
print(a)

}
is also guaranteed to print "hello, world". The write to a happens before the receive on c, which happens before the corresponding send on c completes, which happens before the print.

If the channel were buffered (e.g., c = make(chan int, 1)) then the program would not be guaranteed to print "hello, world". (It might print the empty string, crash, or do something else.)

The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes.

This rule generalizes the previous rule to buffered channels. It allows a counting semaphore to be modeled by a buffered channel: the number of items in the channel corresponds to the number of active uses, the capacity of the channel corresponds to the maximum number of simultaneous uses, sending an item acquires the semaphore, and receiving an item releases the semaphore. This is a common idiom for limiting concurrency.

This program starts a goroutine for every entry in the work list, but the goroutines coordinate using the limit channel to ensure that at most three are running work functions at a time.

var limit = make(chan int, 3)

func main() {

for _, w := range work {
    go func(w func()) {
        limit <- 1
        w()
        <-limit
    }(w)
}
select{}

}

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题