定时器Timer:结构: —— 只能单次定时
type Timer struct {
C <-chan Time // 读 C 阻塞,当定时时间到时,系统自动写入当前时间。阻塞解除。
r runtimeTimer
}
使用方法:
1. 创建 Timer : time.NewTimer()
2. 读 Timer 成员 C 。直到系统时间。
延时定时:
1. sleep(时间)
2. newTimer(时间) <-C
3. <-time.After(时间) 【重点】
定时器停止、重置:
1. t1 := time.NewTimer()
t1.stop()
2. t1 := time.NewTimer()
t1.reset(新时间)
Ticker: —— 周期定时。
type Ticker struct {
C <-chan Time
r runtimeTimer
}
特性:一次设定,系统循环写入C 系统时间。
ticker 只有 stop() 没有 reset() 方法
select :监听 channel 上的 数据流动(r、w) 【重点】
语法:
select {
case channnel IO 操作
case channnel IO 操作:
.....
default :
}
select 使用注意事项:
1. case分支中必须是一个 IO操作:
2. 当case分支不满足监听条件,阻塞当前 case 分支
3. 如果同时有多个case分支满足,select随机选定一个执行( select底层实现,case对应一个 goroutine)
4. 一次select 监听,只能执行一个case分支。通常将select放于for 循环中
5. default 在所有case均不满足时,默认执行的分组,为防止忙轮询,通常将 for 里 select中的 default 省略。
【重要结论】:使用 select的 go程,与其他go程间 采用 【异步通信】 通信方式。
斐波那契数列:
1,1,2, 3, 5, 8, 13, 21, 34, 55, 89 。。。 —— 15
select 实现的 超时:【重点】
1. select {
case ch<-:
case <-time.After(时间) :
}
2. 当 select 监听的 ch 写事件满足时。会重置 time.After 的定时器时间。
3. 只有 select 监听的所有case均不满足,time.After 计时满 ,该 case 才满足监听条件(读事件)。
死锁: 运行时错误 —— 在编码期间提早预见,提早规避。
死锁1: 同一个go程,使用一个channel 自己读自己写。
死锁2: 读写位于两个go程间。但是go程的创建在 r 或 w 之后。r 或 w 会造成go程阻塞,导致另一个go程无法创建。
死锁3: 两个go程,两个channel。 go程1 对channel1读,成功写入 channel2. go程2 对channel2读,成功写入 channel1.
互相死锁。
死锁4: channel 和 读写锁、互斥锁。
互斥锁(互斥量):
建议锁。不具有强制性。
保护公共区,被锁住后,只有成功加锁的 go 程能正常访问。其他go程阻塞在 锁的等待事件上。
使用注意:
1. 公共区访问开始之前,加锁
2. 访问结束后 立即解锁。 锁的 粒度 越小越好。
使用方法:
1. 定义 互斥锁。 var mutex sync.Mutex
2. 加锁 mutex.Lock()
。。。 访问公共区数据
3. 解锁 mutex.Unlock()
读写锁:
读共享,写独占。
写锁优先级高于读锁。
使用注意:
1. 公共区访问开始之前,加锁
2. 访问结束后 立即解锁。 锁的 粒度 越小越好。
3. 锁只有一把。但是具备两种加锁属性。读属性加锁。写属性加锁。
使用方法:
1. 定义 读写锁。 var rwmutex sync.RWMutex
2. 加读锁 rwmutex.RLock() 加写锁 rwmutex.Lock()
。。。 读公共区数据 。。。 写公共区数据
3. 解锁 rwmutex.RUnlock() 解写锁 rwmutex.UnLock()
多go程同步时,尽量少 锁(读写锁、互斥锁)、和 channel 【混用】。 —— 条件变量。
条件变量:
Cond 语法:
type Cond struct {
noCopy noCopy
L Locker // 锁:互斥、读写
notify notifyList
checker copyChecker
}
Cond.Wait():
1) 阻塞等待条件变量满足
2)释放已经掌握的互斥锁 (1/2 两步为 原子操作。)
。。。 阻塞等。
3) 被唤醒时,解除阻塞,重新加锁
Cond.Signal():
唤醒阻塞在条件变量上的 一个 对象。
Cond.Broadcast()
唤醒阻塞在条件变量上的 所有 对象。
条件变量使用流程: — 生产者为例
1. 创建条件变量 var cond Sync.Cond —— 结构体
2. 初始化 条件变量使用的 锁 cond.L = new(sysc.Mutex)
3. 给条件变量的 互斥锁 加锁 cond.L.Lock()
4. 判断条件变量是否满足。 调用wait :1) 2) 3)
for len(ch) == cap(ch) { // 此处判断,必须使用 for ,而不能使用 if
cond.Wait()
}
5. 生产数据: 产生随机数 num := rand.Intn()
6. 写入公共区(缓冲区) ch <- num
7. 唤醒对端(消费者) cond.Signal()
8. 给条件变量的 互斥锁 解锁 cond.L.UnLock()