原来分享基础语法的时候,还未分享过 chan 通道,这次把它补上。
chan 可以理解为队列,遵循先进先出的规则。
在说 chan 之前,咱们先说一下 go 关键字。
在 go 关键字后面加一个函数,就可以创建一个线程,函数可以为已经写好的函数,也可以是匿名函数。
举个例子:
func main() {fmt.Println("main start")go func() {fmt.Println("goroutine")}()fmt.Println("main end")}
输出:
main startmain end
为什么没有输出 goroutine ?
首先,我们清楚 Go 语言的线程是并发机制,不是并行机制。
那么,什么是并发,什么是并行?
并发是不同的代码块交替执行,也就是交替可以做不同的事情。
并行是不同的代码块同时执行,也就是同时可以做不同的事情。
举个生活化场景的例子:
你正在家看书,忽然电话来了,然后你接电话,通话完成后继续看书,这就是并发,看书和接电话交替做。
如果电话来了,你一边看书一遍接电话,这就是并行,看书和接电话一起做。
说回上面的例子,为什么没有输出 goroutine ?
main 函数是一个主线程,是因为主线程执行太快了,子线程还没来得及执行,所以看不到输出。
现在让主线程休眠 1 秒钟,再试试。
func main() {fmt.Println("main start")go func() {fmt.Println("goroutine")}()time.Sleep(1 * time.Second)fmt.Println("main end")}
输出:
main startgoroutinemain end
这就对了。
接下来,看看如何使用 chan 。
// 声明不带缓冲的通道ch1 := make(chan string)// 声明带10个缓冲的通道ch2 := make(chan string, 10)// 声明只读通道ch3 := make(<-chan string)// 声明只写通道ch4 := make(chan<- string)
注意:
不带缓冲的通道,进和出都会阻塞。
带缓冲的通道,进一次长度 +1,出一次长度 -1,如果长度等于缓冲长度时,再进就会阻塞。
ch1 := make(chan string, 10)ch1 <- "a"
val, ok := <- ch1// 或val := <- ch1
close(chan)
注意:
func main() {fmt.Println("main start")ch := make(chan string)ch <- "a" // 入 chango func() {val := <- ch // 出 chanfmt.Println(val)}()fmt.Println("main end")}
输出:
main startfatal error: all goroutines are asleep - deadlock!
What ? 这是为啥,刚开始就出师不利呀?
因为,定义的是一个无缓冲的 chan,赋值后就陷入了阻塞。
怎么解决它?
声明一个有缓冲的 chan。
func main() {fmt.Println("main start")ch := make(chan string, 1)ch <- "a" // 入 chango func() {val := <- ch // 出 chanfmt.Println(val)}()fmt.Println("main end")}
输出:
main startmain end
为啥没有输出 a , 和前面一样,主线程执行太快了,加个休眠 1 秒钟,再试试。
func main() {fmt.Println("main start")ch := make(chan string, 1)ch <- "a" // 入 chango func() {val := <- ch // 出 chanfmt.Println(val)}()time.Sleep(1 * time.Second)fmt.Println("main end")}
输出:
main startamain end
这就对了。
再看一个例子:
func main() {fmt.Println("main start")ch := make(chan string)go func() {ch <- "a" // 入 chan}()go func() {val := <- ch // 出 chanfmt.Println(val)}()time.Sleep(1 * time.Second)fmt.Println("main end")}
输出:
main startamain end
再看一个例子:
func producer(ch chan string) {fmt.Println("producer start")ch <- "a"ch <- "b"ch <- "c"ch <- "d"fmt.Println("producer end")}func main() {fmt.Println("main start")ch := make(chan string, 3)go producer(ch)time.Sleep(1 * time.Second)fmt.Println("main end")}
输出:
main startproducer startmain end
带缓冲的通道,如果长度等于缓冲长度时,再进就会阻塞。
再看一个例子:
func producer(ch chan string) {fmt.Println("producer start")ch <- "a"ch <- "b"ch <- "c"ch <- "d"fmt.Println("producer end")}func customer(ch chan string) {for {msg := <- chfmt.Println(msg)}}func main() {fmt.Println("main start")ch := make(chan string, 3)go producer(ch)go customer(ch)time.Sleep(1 * time.Second)fmt.Println("main end")}
输出:
main startproducer startproducer endabcdmain end
就到这吧。