不要经过共享内存来通讯,要经过通讯来共享内存。bash
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。ui
ch := make(chan int)
复制代码
同 map 同样,一个 channel 引用着 make 建立的底层数据结构上,当把 channel 当作函数参数传递时,其实是拷贝一份 reference,也就是说函数内部和外部引用的是相同的数据结构,因此在函数内部能够直接修改 channel 的值。同其它 reference type 同样,channel 的 zero value 是 nil。spa
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,则他们相同:code
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:cdn
var ch02 chan int
if ch02 == nil {
fmt.Println("ch02 is nil") // return
}
复制代码
channel 有三种基本的操做 send、receive、close。
channel 支持 send 操做,意思是向 channel 中发送数据,Go 使用 <-
操做符来实现 send:
ch <- x //send
复制代码
被send的对象是在 <-
在 channel 右侧。
channel 还支持 receive 操做,意思是从 channel 中取出数据,Go 也是使用 <-
操做符来实现 receive:
x, ok := <- ch //receive
复制代码
receive 时 <-
在左侧,若是一个执行 receive 时没有用任何变量来赋值,则该值被抛弃,receive操做能够得到2个参数,ok能够用来判断channel是否关闭,从一个已关闭的channel获取值会获得零值,陷入死循环
receive 的这个操做经常被用来作状态同步:
<- ch
复制代码
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 循环将结束。