并发模型有进程级别的,有IO多路复用的,有线程的,有协程级别的。解释起来很啰嗦,参考第一条有一篇博文描述的挺形象的可以参考下。
goroutine
起一个goroutine
非常简单,go
后面跟执行的func
即可。
go func()
需要注意任意一个goroutine panic
都会使程序崩溃,所以注意recover
(⊙o⊙)哦
defer func(){
err := recover()
xxxx
}
channel
字如其意,即通道。专门用来传输信息,然后是堵塞型的,即一次一条信息。
ch := make(chan int64) // 声明
defer close(ch) // 最后关闭 channel
ch <- variable // 发送
variable := <- ch // 接收
variable, isOpen:= <- ch // 接收并返回 channel 是否已关闭
make
第二个参数可以穿一个数字,代表缓存多少条消息,即不会像不设置时发送一条即堵塞等待接收。如果channel
关闭后再执行发送动作,则会panic
,接收者再去读取则会收到零值。
正常业务场景中会有同时执行好几个操作的时候(非幂等操作哦),这时候就可以用goroutine
去分别执行,然后使用channel
异步接收所有数据。
for {
v := <- ch
}
这里用for
来等待无限循环通道,指定数量只要设置for
的终止条件即可。如果所有goroutine
都已经关闭,而这里还未终止程序可是会报错的哦,所以一定要有一个终止条件。
select
ok 以上一直是一个channel
,有的时候呢需要多个channel
。
for {
select {
case <- ch:
// xxx
case <- ch2:
// xxx
}
}
sync
sync
提供了一些魔术方法以便于我们简单快速的使用goroutine
WaitGroup
wg sync.WaitGroup
wg.Add(2)
go func1()
go func2()
func1() {
defer wg.Done() // 告知已执行完毕一个
// 逻辑
}
wg.wait() // 堵塞住,等待 wg.Done() 得到的信号数等于 Add 的数量
WaitGroup
就像一个经过简单封装的线程安全的方法
atomic
当有多个goroutine
想直接访问操作变量,而不使用通信时,就可能会出现资源竞争,以至于死锁。所以atomic
提供了AddInt64, LoadInt64, StoreInt64
等函数,这些函数可以保证在访问相同资源时,变成同步代码,排队去操作相同的资源。
mutex
另外还有一种互斥锁,mutex.Lock(), mutex.Unlock()
两个函数之间的代码将会成为一个独特的区域,同一时刻只会有一个goroutine
在执行这段代码。
以上两个就分别类似于其它语言的安全方法,锁机制。