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() }

 

posted @ 2021-03-02 21:49  浪涛飞  阅读(144)  评论(0编辑  收藏  举报