Go语言基础之并发

80 阅读3分钟
### goroutine调度
目前 Go 语言的调度器采用的是 `GPM` 调度模型
单从线程调度讲,Go语言相比起其他语言的优势在于OS线程是由OS内核来调度的,
goroutine 则是由Go运行时(runtime)自己的调度器调度的,完全是在用户态下完成的,
不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池, 
不直接调用系统的malloc函数(除非内存池需要改变),成本比调度OS线程低很多。
另一方面充分利用了多核的硬件资源,近似的把若干goroutine均分在物理线程上,
再加上本身 goroutine 的超轻量级,以上种种特性保证了 goroutine 调度方面的性能。

## channel(单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义)
Go语言采用的并发模型是`CSP(Communicating Sequential Processes)`,提倡**通过通信共享内存**而不是**通过共享内存而实现通信**。

### channel类型
var 变量名称 chan 元素类型
未初始化的通道类型变量其默认零值是`nil`(未赋值)

### 初始化channel
声明的通道类型变量需要使用内置的`make`函数初始化之后才能使用。具体格式如
make(chan 元素类型, [缓冲大小])

### channel操作
通道共有发送(send)、接收(receive)和关闭(close)三种操作而发送和接收操作都使用`<-`符号
1.  对一个关闭的通道再发送值就会导致 panic1.  对一个关闭的通道进行接收会一直获取值直到通道为空。
1.  对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
1.  关闭一个已经关闭的通道会导致 panic。

### 无缓冲的通道(同步通道
首先无缓冲通道`ch`上的发送操作会阻塞,直到另一个 goroutine 在该通道上执行接收操作,这时数字10才能发送成功,两个 goroutine 将继续执行。相反,如果接收操作先执行,接收方所在的 goroutine 将阻塞,直到 main goroutine 中向该通道发送数字10。)
func recv(c chan int) {
	ret := <-c
	fmt.Println("接收成功", ret)
}

func main() {
	ch := make(chan int)
	go recv(ch) // 创建一个 goroutine 从通道接收值
	ch <- 10
	fmt.Println("发送成功")
}

### 有缓冲的通道
我们可以使用内置的`len`函数获取通道内元素的数量,使用`cap`函数获取通道的容量

### 多返回值模式
通道内的值被接收完后再对通道执行接收操作得到的值会一直都是对应元素类型的零值。
那我们如何判断一个通道是否被关闭了呢?
1、对一个通道执行接收操作时支持使用如下多返回值模式。
value, ok := <- ch
2for range接收值.


Go语言中提供了**单向通道**来处理这种需要限制通道只能进行某种操作的情况
<- chan int // 只接收通道,只能接收不能发送
chan <- int // 只发送通道,只能发送不能接收
函数传参及任何赋值操作中全向通道(正常通道)可以转换为单向通道,但是无法反向转换

## select多路复用
select {
case <-ch1:
	//...
case data := <-ch2:
	//...
case ch3 <- 10:
	//...
default:
	//默认操作
}

-   可处理一个或多个 channel 的发送/接收操作。
-   如果多个 case 同时满足,select 会**随机**选择一个执行。
-   对于没有 caseselect 会一直阻塞,可用于阻塞 main 函数,防止退出

### sync.Mutex
### sync.RWMutex
### sync.WaitGroup
### sync.Once

Go 语言中内置的 map 不是并发安全的

## 原子操作
sync/atomic
不可分割性:原子操作在执行时不会被其他 goroutine 中间打断。
并发安全性:由于操作是原子的,因此可以在并发环境下安全地使用,无需额外的同步机制(如锁)。