go语言中可以轻松构建很多的协程,我们今天就说一下如果让这么多的协程之间进行通信-channel

还记得之前一篇文章(go语言并发之MPG模型)中我们说的,要通过通信来共享内存,channel就是来做这个事情的,chan 是go语言内置的数据类型之一,它就像一个类型安全的通用的管道,既可以同步两个并发执行的函数,也可以通过管道来传递数据来通信,我们可以声明一个 chan,以及里面可以传输的数据的数据类型,并使用 “<-”和“->”来从chan中读取和写入数据,这里我不会将具体的代码写出来,因为这和普通的声明变量没有太大区别,大家学习过go的都会使用。

chan可以在多个goroutine中通信,而且我们对chan的操作也是同步的。对于同一个chan,同一时刻我们只能在一个goroutine中向chan写数据,同样也只有一个goroutine来读数据。chan中的数据类似一个队列,FIFO(先进先出),而且如果一个数据从chan中被读取走了,那么这个数据就会从这个chan中删除掉。

chan有两种,一种是有 缓存 的,一种是无缓存的。这个在我们创建一个chan的时候就要声明,而且如果是缓存的还要指定chan缓存的大小:

c := make(chan string, 8) // 声明了一缓存的chan,可以缓存8个string
 

对于缓存的chan,我们可以向里面写入多个数据,即使没有其他的goroutine读取数据。但是数据达到了缓存的上线,那么再写入数据就会堵塞,除非有其他的goroutine读取数据。

对于非缓存的chan,我们写入数据后需要有其他goroutine读取,否则就会堵塞了。

chan还有一种是单向的chan,顾名思义就是一个只能读取或者只能写入数据的chan,但是我们不应该在声明一个chan的时候就指定为单向chan,否则我们就没办法用了,那么我们什么时候需要用单向chan呢?

在函数里面我们经常用到单向的chan,例如下面的函数:

package main
import "fmt"
func main() {
 c := make(chan int)
 go testChan(c)
 c <- 1
}
func testChan(c <-chan int) {
 v := <-c
 fmt.Println(v)
}
 

我们在函数中传进去了一个单向的只能读取数据chan:c,在这个函数中我们只可以读取其中的数据。但是我们在调用函数时,穿进去的可以是一个双向的chan,也就是说,我们传进去一个chan,由于函数参数中奖这个双向的chan声明为了单向只读的chan,所以在这个函数中,这个chan只能有读取数据的操作。


我们再说一下select语言,这个是只能用于对chan读写操作的语句,类似switch,但是每个case必须是chan的读或者写操作。对于多个case,如果同时有多个case执行操作(或者说多个chan进行了读取或者写入数据的操作),那么select语句会随机执行其中一个,如果只有一个case后面的chan有操作,那么就毫无疑问的执行这个了。我们可以再加上default语句,也就是如果所有case都不执行就走default语句了。

我们可以使用select语句实现timeout:

func timeout(timeout time.Duration, c0,c1 chan int){
 select {
 case <-time.After(timeout * time.Second):
 fmt.Println("timeout")
 return
 case value := <-c1:
 fmt.Println(value)
 case value:= <-c1:
 fmt.Println(value)
 }
}
 

上面代码中,如果c0和c1一直没有数据,那么时间到了超时时间,就会打印timeout并且退出了。

今天就说这么多,大家觉得满意就转发或者评论点赞都可以~