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通信来实现。
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") }
-