一句话总结:sync.WaitGroup用于等待一个系列执行完成,不能保证数据的原子性操作。
go语言中有两种同步机制,一种是channel,另一种是锁机制。sync.WaitGroup是一种比较简单的同步机制,接口简洁只有三个,Add(),Done(),Wait();其中Add()是添加计数,Done()是减去一个计数,本质是Add(-1),Wait是阻塞等待计数为0;
// Done decrements the WaitGroup counter.
func (wg *WaitGroup) Done() {
wg.Add(-1)
}
看一个没有同步的例子,很明显计数最后没有达到10
package main
import (
"fmt"
_ "sync"
_ "time"
)
var g_cnt int = 0
func main() {
for i := 0; i < 10; i++ {
go func(i int) {
g_cnt++
fmt.Println("i:", i)
}(i)
}
fmt.Println("g_cnt:", g_cnt)
}
结果:
g_cnt: 2
i: 0
i: 5
i: 2
i: 1
成功: 进程退出代码 0.
很明显主进程退出,goroutine没有全部执行完成就终止了。
package main
import (
"fmt"
_ "sync"
"time"
)
var g_cnt int = 0
func main() {
for i := 0; i < 1000; i++ {
go func(i int) {
g_cnt++
fmt.Println("i:", i)
}(i)
}
time.Sleep(10 * time.Second)
fmt.Println("g_cnt:", g_cnt)
}
结果:
...........
i: 994
i: 989
i: 990
i: 995
i: 991
i: 999
i: 997
i: 985
i: 947
i: 998
g_cnt: 990
成功: 进程退出代码 0.
加了10s延时后所有goroutine都运行结束,由于没有同步机制,最后基数没有到达1000.
package main
import (
"fmt"
"sync"
_ "time"
)
var g_cnt int = 0
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
go func(i int) {
wg.Add(1)
defer wg.Done()
g_cnt++
fmt.Println("i:", i)
}(i)
}
wg.Wait()
结果:
.........
i: 977
i: 991
i: 983
i: 996
i: 992
i: 984
i: 993
i: 997
i: 982
i: 998
g_cnt: 996
成功: 进程退出代码 0.
可以看出,waitgroup只是保证了所有goroutine执行结束,从字面上看WaitGroup就是指等待一组,等待一个系列执行完成后才会继续向下执行。所有不能保证数据的原子性操作。
package main
import (
"fmt"
"sync"
_ "time"
)
var g_cnt int = 0
func main() {
var wg sync.WaitGroup
var mutex sync.RWMutex
for i := 0; i < 1000; i++ {
go func(i int) {
wg.Add(1)
defer wg.Done()
mutex.Lock()
defer mutex.Unlock()
g_cnt++
fmt.Println("i:", i)
}(i)
}
wg.Wait()
fmt.Println("g_cnt:", g_cnt)
}
结果:
......
: 988
i: 995
i: 540
i: 996
i: 990
i: 997
i: 991
g_cnt: 1000
成功: 进程退出代码 0.
结果读写锁后发现每次结果都是1000.