Go同步原语
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() }