了解+整理sync.WaitGroup的用法,并记录一道随机面试题目(第一次见到golang相关的面试题.
Motivation
当在go程序中实现多线程时,经常需要通过time.sleep
的方式使主程序等待其他线程完成。但利用sleep使主程序idle资源开销较大,并且无法判断需要等待的有效时长.
此处可以通过使用channel的方式来使主程序等待所有其他线程完成任务.
1 2 3 4 5 6 7 8 9 10 11 12 func main () { c := make (chan bool ) for i := ; i < ; i++ { go func (i int ) { fmt.print (i) c <- true }(i) } for i:=; i <; i++ { <-c } }
使用channel可以完美解决等待时长随机的问题,但内存开销较大,有大炮打蚊子的嫌疑(。
sync.WaitGroup
则能更加方便高效地帮助我们实现这个需求。
Intro
WaitGroup
内置counter从0开始,包含三个方法Add(), Done(),Wait()
用来更新和控制计数器的数量。
Add(n)
:将计数器设置为n,注意n不能是负数
Done()
:对计数器进行-1操作,相当于Add(-1);由于n不能为负,注意Done的调用次数也不可以超过n的总数
Wait()
:在计数器没有归0前将会阻塞代码的运行
使用sync.WaitGroup
可以将代码修改成这样:
1 2 3 4 5 6 7 8 9 10 11 func main () { wg := sync.WaitGroup{} wg.Add(n) for i := ; i < ; i++ { go func (i int ) { fmt.print (i) wg.Done() }(i) } wg.Wait() }
Notice : WaitGroup对象不可引用,需要在函数传入值时使用address:
1 2 3 4 5 6 7 8 9 10 11 12 13 unc main() { wg := sync.WaitGroup{} wg.Add(n) for i := ; i < ; i++ { go counter(i, &wg) } wg.Wait() } func counter (i int , wg * sync.WaitGroup) { fmt.print (i) wg.Done() }
Interview Question
如何利用两个go routine交替打印1-100的奇偶数?(输出时需按照从小到大顺序输出)
利用sync.WaitGroup
首先使主程序保持执行状态
在两个go routine中实现输出后对计数器执行减1操作,因此可以实现互相堵塞交替输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport ( "fmt" "sync" ) func main () { var wg sync.WaitGroup for i := 1 ; i <= 100 ; i++ { if i%2 == 0 { wg.Add(1 ) go func (i int ) { defer wg.Done() fmt.Println("Routine 1 printing even:" , i) }(i) wg.Wait() } else { wg.Add(1 ) go func (i int ) { defer wg.Done() fmt.Println("Routine 2 printing odd:" , i) }(i) wg.Wait() } } }