在Go语言的sync包中,提供了基本的同步单元,如互斥锁。除了Once和WaitGroup类型,⼤部分都是适⽤于低⽔平程序线程,高水平的同步使⽤channel通信更好⼀些。
我们可以通过WaitGroup里的wait()方法,来阻塞主线程,直到其他子线程或协程运行完毕,再回到主线程。
WaitGroup同步等待组
type WaitGroup struct {
noCopy noCopy
state1 [12]byte
sema uint32
}
WaitGroup⽤于等待⼀组线程或协程的结束。⽗线程调⽤Add⽅法来设定应等待的线程数量。每个被等待的线程在结束时应调⽤Done⽅法。同时,主线程⾥可以调⽤Wait⽅法阻塞自身,等待Add()方法里声明的线程运行至结束,再返回主线程。
WaitGroup里的方法
- func (wg *WaitGroup) Add(delta int)
Add⽅法向内部计数加上delta, delta可以是负数;如果内部计数器变为0,Wait⽅法阻塞等待的所有线程都会释放;如果计数器⼩于0,⽅法panic。注意,Add加上正数的调⽤应在Wait之前,否则Wait可能只会等待很少的线程。⼀般来说,Add()⽅法应在创建新的线程,或者其他应等待的事件之前调用。 - func (wg *WaitGroup) Done()
Done⽅法减少WaitGroup计数器的值,应在线程的最后执行。 - func (wg *WaitGroup) Wait()
Wait⽅法阻塞直到WaitGroup计数器减为0。
案例1. WaitGroup通过wait()阻塞主函数,来运行3个子协程。
//myWaitGroupDes.go
// myWatiGroupDes project main.go
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func printNumA(wg *sync.WaitGroup) {
k := 0
for i := 11; i <= 20; i += 2 {
fmt.Printf("子协程A[%d]= %d \n", k, i)
time.Sleep(time.Duration(rand.Intn(1000)))
k++
}
wg.Done() //计算器减1
}
func printNumB(wg *sync.WaitGroup) {
k := 0
for i := 22; i <= 30; i += 2 {
fmt.Printf("子协程B[%d]= %d \n", k, i)
time.Sleep(time.Duration(rand.Intn(1000)))
k++
}
wg.Done() //计算器减1
}
func printNumC(wg *sync.WaitGroup) {
k := 0
for i := 'a'; i <= 'e'; i++ {
fmt.Printf("子协程C[%d]= %c \n", k, i)
time.Sleep(time.Duration(rand.Intn(1000)))
k++
}
wg.Done() //计算器减1
}
func main() {
var wg sync.WaitGroup
fmt.Printf("%T\n", wg)
fmt.Println(wg)
wg.Add(3)
rand.Seed(time.Now().UnixNano())
go printNumA(&wg)
go printNumB(&wg)
go printNumC(&wg)
wg.Wait()
fmt.Println("main解除阻塞,main is over...")
}
效果如下:
