不要通过共享内存来通信,要通过通信来共享内存。
Go 支持两种方式的并发模型: communicating sequential processes(CSP) 和 shared memory multithreading,前者是 goroutine 和 channel 并发模型实现的基础,后者是传统的共享内存的方式,也就是多线程模型。
如何理解 CSP ?简单来说就是通过在不同的 goroutine 之间传递 value 来维护并发的下不同 goroutine 的状态,但是对变量的使用、修改要限制在单一的 goroutine 中。
定义
在 Go 中可以并发执行的活动单元称之为 goroutine。当一个 Go 程序启动时,一个执行 main function 的 goroutine 会被创建,称之为 main goroutine
。创建新的 goroutine 可以使用 go 语句,像这样: go f(),其中 f
是一个函数。使用 go 语句开启一个新的 goroutine 之后,go 语句之后的函数调用将在新的 goroutine 中执行,而不会阻塞当前的程序执行。
如果说 goroutine 是并发执行的一个 Go program, channel 就是它们之间的连接通道,它提供了 goroutine 之间相互通信的机制。channel 是有类型的,channel 中使用的 type 称之为 element type,比如 int 类型的 channel 写作为 chan int
。
Go 使用 make 内建函数创建 channel。
ch := make(chan int)
复制代码
同 map 一样,一个 channel 引用着 make 创建的底层数据结构上,当把 channel 当做函数参数传递时,实际上是拷贝一份 reference,也就是说函数内部和外部引用的是相同的数据结构,所以在函数内部可以直接修改 channel 的值。同其它 reference type 一样,channel 的 zero value 是 nil。
channel 是可比较的
channel 是可比较的,如果两个 channel 的类型相同,它们可以彼此相互比较:
ch01 := make(chan int)
ch02 := make(chan int)
if ch01 == ch02 {
fmt.Println("ch01 == ch02")
} else {
fmt.Println("ch01 != ch02") // return
}
复制代码
两个不是 nil 的 channel 比较实际上比较的他们的 reference 是否相同,如果他们都引用同一个 channel,则他们相同:
func main() {
ch01 := make(chan int)
func02(ch01, ch01)
}
func func02(a chan int, b chan int) {
if a == b {
fmt.Println("a == b") // return
}
}
复制代码
当然 channel 也可以和 nil 比较,没有初始化的 channel 就是 nil:
var ch02 chan int
if ch02 == nil {
fmt.Println("ch02 is nil") // return
}
复制代码
channel 的基本操作
channel 有三种基本的操作 send、receive、close。
send
channel 支持 send 操作,意思是向 channel 中发送数据,Go 使用 <-
操作符来实现 send:
ch <- x //send
复制代码
被send的对象是在 <-
在 channel 右侧。
receive
channel 还支持 receive 操作,意思是从 channel 中取出数据,Go 也是使用 <-
操作符来实现 receive:
x, ok := <- ch //receive
复制代码
receive 时 <-
在左侧,如果一个执行 receive 时没有用任何变量来赋值,则该值被抛弃,receive操作可以获得2个参数,ok可以用来判断channel是否关闭,从一个已关闭的channel获取值会得到零值,陷入死循环
receive 的这个操作常常被用来做状态同步:
<- ch
复制代码
close
channel 还支持第三种操作 close
,如果 channel 被 close,表明 channel 不会再 send 任何值了,如果还继续对 channel 执行 receive 操作,当 channel 中的值消耗完毕之后,之后返回的是对应 element type 的 zero value,如果对 channel 执行 send 操作,将会引起 panic:
close(ch)
ch <- x // panic
复制代码
close 操作常常和 for 语句配合使用,表示一个 channel 不再产生新的值:
for x := range ch {
fmt.Print(x)
}
复制代码
close channel 之后,for 循环将结束。
有疑问加站长微信联系(非本文作者)