Golang里channel为什么不会阻塞?

烧麦
  • 200

最近在学通道channel,发现一个简单的demo:

package main

import "fmt"

func main() {
    chanInt := make(chan int)
    go func() {
        chanInt <- 100
    }()

    res := <-chanInt
    fmt.Println(res)
}

输出结果是100,这个没有问题。但是之前在学goroutine的时候有看到过一个例子:

package main

import "fmt"

func hello() {
    fmt.Println("Hello Goroutine!")
}
func main() {
    go hello() // 启动另外一个goroutine去执行hello函数
    fmt.Println("main goroutine done!")
}

这个例子输出的只有:main goroutine done! 并没有Hello Goroutine!
看过解释:在程序启动时,Go程序就会为main()函数创建一个默认的goroutine。当main()函数返回的时候该goroutine就结束了,所有在main()函数中启动的goroutine会一同结束

那么这个解释放到第一个例子为什么不适用了?

ps:我得理解是:运行到res := <-chanInt这句会阻塞,直到协程写入通道后,就马上读取,继续执行打印语句。不知道理解的对不对?

然后就是关于阻塞的情况,比如我把第一个例子改一下:

package main

import (
    "fmt"
    "time"
)

func main() {
    chanInt := make(chan int)
    go func() {
        chanInt <- 100
    }()
    time.Sleep(10 * time.Second)
    res := <-chanInt
    fmt.Println(res)
}

多了time.Sleep(10 * time.Second)等待10秒钟,10秒后输出100,这个没有问题。

然后再看一个例子:

func main() {
    chanInt := make(chan int)
    chanInt <- 100
    res := <-chanInt
    fmt.Println(res)
}

这个例子就会死锁,阻塞在chanInt <- 100这句,发现没有接收者所以死锁了,这边不理解的是:睡眠10秒的时候,在这10秒里面为什么不会造成死锁?而最后一个例子一运行马上报死锁?

回复
阅读 1.6k
4 个回答

sleep这个例子的常见执行顺序是:

  1. 协程main Sleep 10s
  2. 协程2 chanInt <- 100,阻塞
  3. 协程main res:<-chanInt 读取到数据
  4. 协程main 执行其他操作

没有sleep的例子中,堵塞在 chanInt <- 100,不再往下执行

例子中的chan没有设定缓存数量,那么写入chan时,如果没有协程正在读,那么就会堵塞,直到有协程开始读取数据

channel 没有数据时收取操作会阻塞,这个没错。

最后一个例子,因为main goroutine 阻塞在 channel 收取操作上了,而且现在没有其他的 goroutine,所以 main goroutine 永远不可能解除阻塞。如果有另外的 goroutine 在运行,它就有可能向 channel 发送数据,让 main 解除阻塞,就像前面的例子一样

func main() {
    chanInt := make(chan int)
    chanInt <- 100
    res := <-chanInt
    fmt.Println(res)
}

这段代码,很多同学估计会想,我往channel里写数据,再拿出来,怎么就会死锁呢?这是因为该channel是个无缓冲的channel,你往里写东西的时候,必须得有接收方。如果想让上面的代码成功运行。要改为:

func main() {
    chanInt := make(chan int, 1)
    chanInt <- 100
    res := <-chanInt
    fmt.Println(res)
}

channel变成带缓冲的channel,这样就可以在一个协程里发送完了,再接收。

lxyz123
  • 2
新手上路,请多包涵

image.png

宣传栏