### 上周的课程我们了解到了原子锁和互斥锁,都可以保证共享数据的读写,但是它们还是有点复杂,而且影响性能,对此,Go又为我们提供了一种工具,这就是通道,goroutine 运行的时候是相互独立的。使用关键字 go 创建 goroutine 来运行函数。
### 一个死锁的例子:
`
var ch1 chan int = make(chan int)
var ch2 chan int = make(chan int)
func say(s string) {
fmt.Println(s)
ch1 <- <- ch2 // ch1 等待 ch2流出的数据
}
func main() {
go say("hello")
<- ch1 // 堵塞主线
}
`
### 所以在多个goroutine并发中,我们可以通过使用通道,在多个goroutine发送和接受共享的数据,达到数据同步的目的。
### 通道,他有点像在两个goroutine之间架设的管道,一个goroutine可以往这个管道里塞数据,另外一个可以从这个管道里取数据,有点类似于我们说的队列。
`
通道的声明方式 ch:=make(chan int)
`
### 通道类型和Map这些类型一样,可以使用内置的 make 函数声明初始化,这里我们初始化了一个 chan int 类型的通道,所以我们只能往这个通道里发送 int 类型的数据,当然接收也只能是 int 类型的数据。
### 通道是用于在goroutine之间通信的,它具有发送和接收两个操作。
`ch <- 2 //发送数值2给这个通道
x:=<-ch //从通道里读取值,并把读取的值赋值给x变量
<-ch //从通道里读取值,然后忽略
`
### 我们使用内置的 close 函数来关闭通道
### select总结
select是Go中的一个控制结构,类似于switch语句。select可以监听多个channel的读写事件,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。
1、如果有多个case都可以运行,select中对case语句的判断不是顺序进行的,select会随机公平地选出一个执行,其他不会执行。
2、如果没有可运行的case语句,且有default语句,那么就会执行default的动作。
3、如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可以运行