package main
/**
同步原语:
sync.Mutex:互斥锁,指的是同一时刻只有一个携程执行某段代码,其他协程都要等待该协程执行完毕后才能继续执行
sync.RWMutex:读写锁
sync.WaitGroup:用于最终完成的场景,关键点一定要等待所有的协程都执行完毕
sync.Once:只执行一次,调用方法为 var once sync.Once once.Do(函数或方法)
sync.Cond:用于发号施令,一声令下所有协程都可以开始执行,关键点在于协程开始的时候是等待的,要等待sync.Cond唤醒才能执行
*/
import (
"fmt"
"sync"
"time"
)
//共享的资源
var (
sum int
//mutex sync.Mutex
mutex sync.RWMutex
wg sync.WaitGroup
)
func main() {
////因为要监控110个协程,所以设置计数器为110
//wg.Add(110)
////开启100个协程让sum+10
//for i := 0; i < 100; i++ {
// //go add(10)
// go func(i int) {
// //计数器值减1
// defer wg.Done()
// add(i)
// }(10)
//}
//
////启动十个协程用来读
//for i := 0; i < 10; i++ {
// //go fmt.Println("和为:", readSum())
// go func() {
// //计数器值减1
// defer wg.Done()
// fmt.Println("和为:", readSum())
// }()
//}
//
////防止提前退出
////time.Sleep(2 * time.Second)
////一直等待,直到计数器值为0
//wg.Wait()
////fmt.Println("和为:", sum)
//
////在高并发的情况下,sync.Once也会保证onceBody函数只执行一次
//doOnce()
race()
}
func add(i int) {
mutex.Lock()
defer mutex.Unlock()
sum += i
}
//增加一个读取sum的函数,便于演示并发
func readSum() int {
//mutex.Lock()
//defer mutex.Unlock()
//只获取读锁
mutex.RLock()
defer mutex.RUnlock()
b := sum
return b
}
func doOnce() {
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
//用于等待协程执行完毕
done := make(chan bool)
//启动10个协程执行once.Do(onceBody)
for i := 0; i < 10; i++ {
go func() {
//把要执行的函数(方法)作为参数传递给once.Do方法即可
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
}
/**
执行步骤拆解:
1.通过 sync.NewCond 函数生成一个*sync.Cond,用于阻塞和唤醒协程
2.然后启动10个协程模拟10个人,准备就位后调用cond.Wait()方法阻塞当前协程等待发令枪响,这里需要注意的是调用cond.Wait()方法时需要加锁
3.time.Sleep用于等待所有人都进入wait阻塞状态,这样裁判才能调用cond.Broadcast()发号施令
4.裁判准备完毕后,就可以调用cond.Broadcast()通知所有人开始跑了
sync.Cond 有三个方法:
1.Wait,阻塞当前协程,直到被其他协程调用Broadcast或者Signal方法唤醒,使用的时候需要加锁,使用sync.Cond中的锁即可,也就是L字段
2.Signal,唤醒一个等待时间最长的协程
3.Broadcast,唤醒所有等待的协程
注意:在调用Signal或者Broadcast之前,要确保目标协程处于Wait阻塞状态,不然会出现死锁问题
这里和java的等待唤醒机制很像,它的三个方法Wait、Signal、Broadcast就分别对java中的wait、notify、notifyAll
*/
//10个人赛跑,一个裁判发号施令
func race() {
cond := sync.NewCond(&sync.Mutex{})
var wg1 sync.WaitGroup
wg1.Add(11)
for i := 0; i < 10; i++ {
go func(num int) {
defer wg1.Done()
fmt.Println(num, "号已经就位")
cond.L.Lock()
cond.Wait()
fmt.Println(num, "号开始跑...")
cond.L.Unlock()
}(i)
}
//等待所有goroutine都进入wait状态
time.Sleep(2 * time.Second)
go func() {
defer wg1.Done()
fmt.Println("裁判已经到位,准备发令枪")
fmt.Println("比赛开始,大家准备跑")
cond.Broadcast() //发令枪响
}()
//防止函数提前返回退出
wg1.Wait()
}