多路复用
Go语言中提供了一个关键字select
,通过select
可以监听channel
上的数据流动。select的用法与switch语法类似,由select开始一个新的选择块,每个选择条件由case语句来描述。只不过,select的case
有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作。
select 语法如下:
|
|
在一个select语句中,会按顺序从头至尾评估每一个发送和接收的语句;如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况:⑴ 如果给出了default语句,那么就会执行default语句,同时程序的执行会从select语句后的语句中恢复。⑵ 如果没有default语句,那么select语句将被阻塞,直到至少有一个channel可以进行下去。
在一般的业务场景下,select不会用default
,当监听的流中再没有数据,IO操作就 会阻塞现象,如果使用了default
,此时可以出让CPU时间片。如果使用了default
就形成了非阻塞状态,形成了忙轮训,会占用CPU、系统资源。
阻塞与非阻塞使用场景
- 阻塞: 如:在监听超时退出时,如果100秒内无操作,择退出,此时添加了default会形成忙轮训,超时监听变成了无效。
- 非阻塞: 如,在一个只有一个业务逻辑处理时,主进程控制进程的退出。此时可以使用default。
定时器
Go语言中定时器的使用有三个方法
time.Sleep()
time.NewTimer()
返回一个时间的管道, time.C 读取管道的内容time.After(5 * time.Second)
封装了time.NewTimer(),反回了一个time.C
的管道
示例
|
|
锁和条件变量
Go语言中为了解决协程间同步问题,提供了标准库代码,包sync
和sync/atomic
中。
互斥锁
互斥锁是传统并发编程对共享资源进行访问控制的主要手段,它由标准库sync中的Mutex结构体类型表示。sync.Mutex类型只有两个公开的指针方法,Lock和Unlock。Lock锁定当前的共享资源,Unlock进行解锁。
|
|
读写锁
读写锁的使用场景一般为读多写少,可以让多个读操作并发,同时读取,但是对于写操作是完全互斥的。也就是说,当一个goroutine进行写操作的时候,其他goroutine不能进行读写操作;当一个goroutine获取读锁之后,其他的goroutine获取写锁都会等待
|
|
可以看出,读写锁控制下的多个写操作之间都是互斥的,并且写操作与读操作之间也都是互斥的。但是,多个读操作之间不存在互斥关系。
Go语言中的死锁
死锁 deadlock
是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
单gorutine同时读写,写死锁
在一个gorutine中,当channel无缓冲,写阻塞,等待读取导致死锁
解决,应该至少在2个gorutine进行channle通讯,或者使用缓冲区。
|
|
多gorutine使用一个channel通信,写先于读
代码顺序执行时,写操作阻塞,导致后面协程无法启动进行读操作,导致死锁
|
|
多channel交叉死锁
在goroutine中,多个goroutine使用多个channel互相等待对方写入,导致死锁
|
|
隐性死锁
尽量不要将 互斥锁、读写锁 与 channel 混用情况下,让读先进行读时,因为没写入被阻塞,无法解除。写入时,因为没有读出被阻塞,锁无法解除,导致无数据输出,形成隐形死锁。此时编译器是不报错的。
|
|
Context 上下文
context定义了上下文类型,该类型在API边界之间以及进程之间传递截止时间,取消信号和其他请求范围的值。当在对请求传入一个上下文,可以选择将其替换为使用WithCancel
,WithDeadline
,WithTimeout
。在取消后,从该context处派生的所有子请求也会被取消。
Context的结构体
- Deadline() 返回context的截止时间。
- Done() 返回一个channle,当timeout或cancelfuc将会close(chan)
- Err() 返回错误,未关闭Done()返回nil,取消,返回
"context canceled"
, Deadline返回超时 - Value 返回值。
|
|
演示使用可取消的上下文。可在函数结束时defer cancel()
防止goroutine
的泄露。
|
|
超时处理,WithTimeout
当时间到达设置的时间后退出,也可以使用cancelFunc()
退出处理
|
|
WithDeadline,在标准库中可以看出,实际上WithTimeout是封装了WithDeadline。其功能也是超时退出。
|
|
Context总结
- Context是Go语言在1.7中加入标准库的,是作为
Goroutine
线程安全,防止线程泄露的上下文管理的操作。 - context包的核心是
Context
结构体。 - Context的常用方法为
WithTimeout()
与WithCancel()
- Context在使用时,不要放在结构体内使用,要以函数的参数进行传递。
- Context是线程安全的,可以在多个
Goroutine
传递,当对其取消操作时,所有Goroutine
都执行取消操作。