GO中sync包自由控制并发示例详解

2022-08-04 19:02:00
目录
资源竞争sync.Mutexsync.RWMutexsync.WaitGroupsync.Oncesync.Cond

资源竞争

channel>

并发时对于共享资源必然会出现抢占资源的情况,如果是对某资源的统计,很可能就会导致结果错误。为保证只有一个协程拿到资源并操作它,可以引入互斥锁 sync.Mutex。

sync.Mutex

互斥锁,指的是并发时,在同一时刻只有一个协程执行某段代码,其他协程只有等待该协程执行完成后才能继续执行。

var (sum int 
 mutex sync.Mutex)
func add(i int){
    mutex.Lock()
    sum+=i
    mute.Unlock()
}

使用 mutex 加锁保护 sum+ =i 的代码片段,这样这个片段区就无法同时被多个协程访问,当有协程进入该片段区,那其他的协程就只有等待,以此保证临界区的并发安全。

sync.Mutex 只有 Lock()和 Unlock() 方法,它们是成对存在的,且Lock后一定要执行Unlock进行释放锁。所以可以使用 defer 语句释放锁,以保证锁一定会被释放。

func add(i int){
    mutex.Lock()
    defer mutex.Unlock()
    sum += i
}

sync.RWMutex

上面例子是对>

func getSum(){
    mutex.Lock()
    defer mutex.Unlock()
    b:=sum
    return b
}

多个协程 goroutine 同时读写的资源竞争问题解决,还需要考虑性能问题,每次读写共享资源都加锁,也会导致性能低。

多个协程并发进行读写时会遇到以下情况:

    写时不能同时读,易读到脏数据读时不能同时写,因为会导致结果不一致读时同时写,因数据不变,无论多少个 goroutine 读都是并发安全

    使用读写锁 sync.RWMutex 优化代码:

    var mutex sync.RWMutex
    func readSum() int {
        mutex.RLock()
        defer mutex.RUnlock()
        b := sum
        return b
    }
    

    读写锁的性能比互斥锁性能好。

    sync.WaitGroup

    为了能够监听所有的协程的执行,一旦所有的goroutine>

      声明一个 sync.WaitGroup ,通过 add 方法增加计数器值,有几个协程就计算几个每个协程结束后就调用 Done 方法,主要是让计数器减1最后调用 Wait 方法一直等待,直到计数器为 0 时,所有跟踪的协程都执行完毕
      func run() {
          var wg sync.WaitGroup
          wg.Add(100)
          for i := 0; i < 100; i++ {
              go func() {
                  defer wg.Done()
                  add(10)
              }()
          }
          wg.Wait()
      }
      

      通过 sync.WaitGroup 可以很好地跟踪协程.

      sync.Once

      sync.Once>

      func main(){
          var once sync.once
          oneFunc := func(){
              println("once func")
          }
          once.Do(oneFunc)
      }
      

      sync.Once 适用于创建某个对象的单例、只加载一次的资源等只执行一次的场景。

      sync.Cond

      使用>

      所以 sync.Cond 具有阻塞协程和唤醒协程的功能。详细的用法:

        通过 sync.NewCond 函数生成一个 *sync.Cond,用于阻塞和唤醒协程调用 cond.Wait() 方法阻塞当前协程等待,需要注意调用 cond.Wait() 方法要加锁调用 cond.Broadcast() 后所有协程才开始执行
        func run() {
            cond := sync.NewCond(&amp;sync.Mutex{})
            var wg sync.WaitGroup
            wg.Add(101)
            for i := 0; i &lt; 100; i++ {
                go func(num int) {
                    defer wg.Done()
                    fmt.Println(num, "号正在 awaiting......")
                    cond.L.Lock()
                    cond.Wait() //等待所有协程准备完成
                    fmt.Println(num, "号开始跑……")
                    cond.L.Unlock()
                }(i)
            }
            // 等待所有的协程都进入 wait 状态
            time.Sleep(2*time.Second)
            go func() {
                defer wg.Done()
                // 所有都准备完成,开始
                cond.Broadcast()
            }()
            // 防止函数提前返回退出
            wg.Wait()
        }

        以上就是GO中sync包自由控制并发示例详解的详细内容,更多关于GO sync包控制并发的资料请关注易采站长站其它相关文章!