Skip to content

Files

Latest commit

9a65322 · Oct 10, 2022

History

History

lesson21

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Oct 10, 2022
Nov 1, 2021
Nov 1, 2021
Nov 1, 2021
Nov 1, 2021

readme.md

并发编程之sync包介绍和sync.WaitGroup

sync包介绍

sync包提供了基本的并发编程同步原语(concurrency primitives or synchronization primitives),例如互斥锁sync.Mutex。sync包囊括了以下数据类型:

  • sync.Cond
  • sync.Locker
  • sync.Map
  • sync.Mutex
  • sync.Once
  • sync.Pool
  • sync.RWMutex
  • sync.WaitGroup

除了sync.Once和sync.WaitGroup这2个类型之外,其它类型主要给一些偏底层的库程序用。业务代码里的goroutine同步,Go设计者是建议通过channel通信来实现。

sync.WaitGroup

WaitGroup是sync包里的一个结构体类型,定义如下

type WaitGroup struct {
    // some fields
}

这个结构体有如下3个方法

  • Add:

    func (wg *WaitGroup) Add(delta int)
  • Done:Done调用会将WiatGroup的计数器减1

    func (wg *WaitGroup) Done()
  • Wait:Wait调用会阻塞,直到WaitGroup的计数器为0

    func (wg *WaitGroup) Wait()

定义一个WaitGroup变量的目的是为了等待若干个goroutine执行完成,主goroutine调用Add方法,指明要等待的子goroutine数量,这些子goroutine执行完成后调用Done方法。同时,主goroutine要调用Wait方法阻塞程序,等WaitGroup的计数器减小到0时,Wait方法不再阻塞。

示例如下:

package main

import (
    "fmt"
    "sync"
    "time"
)

var wg sync.WaitGroup

func worker(id int) {
    /*worker执行完成后,会调用Done将wg计数器减1*/
    defer wg.Done()
    fmt.Printf("worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("worker %d done\n", id)
}

func main() {
    /* wg跟踪10个goroutine */
    size := 10
    wg.Add(size)
    /* 开启10个goroutine并发执行 */
    for i:=0; i<size; i++ {
        go worker(i)
    }
    /* Wait一直阻塞,直到wg的计数器变为0 */
    wg.Wait()
    fmt.Println("end")
}
  • 注意事项

    • WaitGroup不要拷贝传值,如果要显式地把WaitGroup作为函数参数,一定要传指针

      WaitGroup给函数A传值,在函数A内部这个WaitGroup会是一个局部变量,对WaitGroup的操作只会在函数内部生效。示例如下:

      package main
      
      import (
          "fmt"
          "sync"
          "time"
      )
      
      
      func worker(id int, wg sync.WaitGroup) {
          /*worker执行完成后,会调用Done将wg计数器减1*/
          defer wg.Done()
          fmt.Printf("worker %d starting\n", id)
          time.Sleep(time.Second)
          fmt.Printf("worker %d done\n", id)
      }
      
      func main() {
          var wg sync.WaitGroup
          /* wg跟踪10个goroutine */
          size := 10
          wg.Add(size)
          /* 开启10个goroutine并发执行 */
          for i:=0; i<size; i++ {
              go worker(i, wg)
          }
          /* 这个例子里Wait会一直阻塞,因为函数worker内部的Done调用对外部的wg其实不生效*/
          wg.Wait()
          fmt.Println("end")
      }

      程序运行时wg.Wait()会报错:fatal error: all goroutines are asleep - deadlock!

      改为下面的传指针就正常了:

      package main
      
      import (
          "fmt"
          "sync"
          "time"
      )
      
      
      func worker(id int, wg *sync.WaitGroup) {
          /*worker执行完成后,会调用Done将wg计数器减1*/
          defer wg.Done()
          fmt.Printf("worker %d starting\n", id)
          time.Sleep(time.Second)
          fmt.Printf("worker %d done\n", id)
      }
      
      func main() {
          var wg sync.WaitGroup
          /* wg跟踪10个goroutine */
          size := 10
          wg.Add(size)
          /* 开启10个goroutine并发执行 */
          for i:=0; i<size; i++ {
              /*wg传指针给worker*/
              go worker(i, &wg)
          }
          /* Wait会一直阻塞,直到wg的计数器为0*/
          wg.Wait()
          fmt.Println("end")
      }

references