本文主要学习了生产者和消费者模式、协程管道定时任务的应用和WaitGroup并发控制原理以及底层源码解读。
目录
案例一:生产者和消费者模式
案例二:协程管道定时任务的应用
定时器的终止与重置
新的需求:需要每隔固定时间就触发任务
改造成任务队列(任务队列)
WaitGroup并发控制原理以及底层源码解读
需求分析:
(模式介绍)
生产者每秒生产一个商品,并通知物流公司取货
物流公司将商品运输到商铺
消费者阻塞等待商铺到货 需要消耗10次商品
实验原理:
代码实现:
func main() {
storageChan := make(chan Product,1000)
shopChan := make(chan Product,1000)
exitChan := make(chan bool,1000)
for i:=1 ; i<999 ; i++ {
go Producer(storageChan,1000)
}
go Logistics(storageChan,shopChan)
go Consumer(shopChan,exitChan,1000)
if <-exitChan {
fmt.Println("exit")
return
}
}
//商品
type Product struct {
Name string
}
//生产者
func Producer(storageChan chan Product, count int) {
for {
producer := Product{"商品:" + strconv.Itoa(count)}
storageChan <- producer
count--
time.Sleep(time.Second)
fmt.Println("生产了",producer)
if count < 1 {
return
}
}
}
//物流公司
func Logistics(storageChan <-chan Product,shopChan chan<- Product) {
for {
Product := <- storageChan
shopChan <- Product
fmt.Println("运输了",Product)
}
}
//消费者
func Consumer (shopChan <-chan Product , exitChan chan<- bool , count int ) {
for {
Product := <- shopChan
fmt.Println("消费了",Product)
count--
if count < 1 {
exitChan <- true
return
}
}
}
需求分析:
实现原理:
实现代码:
fmt.Println("当前时间",time.Now())
//方式1
timer := time.NewTimer(time.Second * 3)
t := <-timer.C //time.C是只读管道
//方式2
t := <-time.After(time.Second*3) //源码可见 实际上返回的就是NewTimer(d).C
fmt.Println(t)
思考:如果提前完成定时处理的业务,或者需要调整时间。怎么办?
NewTimer Stop Reset
根据一些随机数模拟关闭和重置
代码:
var flag bool = isStopTimer()
func main() {
fmt.Println("当前时间", time.Now())
timer := time.NewTimer(time.Second * 3)
if flag {
timer.Stop() //如果停止还去拿,则报错fatal error: all goroutines are asleep - deadlock!
} else {
t := <-timer.C //time.C是只读管道
fmt.Println(t)
}
}
func isStopTimer() bool {
rand.Seed(time.Now().UnixNano())
//time.Now().UnixNano()当前时间戳,目的是每次运行时得到的随机数均不相同
tempInt := rand.Intn(2) + 18
if tempInt >= 18 {
fmt.Println("已经找到大于18,技术timer", tempInt)
return true
} else {
return false
}
}
实现原理:
ticker只要定义完成,从此刻开始计时,不需要任何其他的操作,每隔固定时间都会触发。
timer定时器,是到固定时间后会执行一次
代码:
var exitChan chan bool
func main() {
var count int = 0
exitChan = make(chan bool,1)
ticker := time.NewTicker(time.Second*1)
go func (){ //匿名函数
for {
t := <-ticker.C
fmt.Println("时间:",t.Format("2006-01-02 03:04:05PM"))
count++
if count > 5 {
exitChan <- true
return
}
}
}() //后面的括号用于调用
// time.Sleep(time.Second *10)
if <-exitChan {
fmt.Println("游戏结束")
}
}
sync包的WaitGroup实现了一个类似任务队列的结构。
可以向队列中加入任务,任务完成会移除,Add() Done() Wait()
如果全部完成,队列会触发阻塞以阻止程序继续运行。
代码:
func main() {
var count int = 0
var waitGroup sync.WaitGroup
waitGroup.Add(2)
//括号里面个数取决于下面WaitGroup使用的个数,如果是2那么还等到两个.Done()在执行.Wait()
ticker := time.NewTicker(time.Second*1)
go func (){ //匿名函数
defer waitGroup.Done()
defer ticker.Stop()
for {
t := <-ticker.C
fmt.Println("时间:",t.Format("2006-01-02 03:04:05PM"))
count++
if count > 5 {
return
}
}
}() //后面的括号用于调用
waitGroup.Wait()
fmt.Println("游戏结束")
}
func (t *Timer) Reset(d Duration) bool
Reset使t重新开始计时,(本方法返回后再)等待时间段d过去后到期。如果调用时t还在等待中会返回真;如果t已经到期或者被停止了会返回假。
timer := time.NewTimer(time.Second*1)
go func (){ //匿名函数
defer waitGroup.Done()
defer timer.Stop()
for {
t := <-timer.C
fmt.Println("timer 时间:",t.Format("2006-01-02 03:04:05PM"))
timer.Reset(time.Second)
count2++
if count2 > 3 {
return
}
}
}() //后面的括号用于调用
WaitGroup是Golang应用开发过程中经常使用的并发控制技术。
WaitGroup可理解为Wait-Goroutine-Group,即等待一组goroutine结束。比如某个goroutine需要等待其他几个goroutine全部完成,那么使用WaitGroup可以轻松实现。
(.add(2)括号里面个数取决于下面WaitGroup使用的个数,如果是2那么还等到两个.Done()在执行.Wait())
实现原理: