sync.WaitGroup
은 동시성 환경에서 매우 일반적인 데이터 구조로, 모든 코루틴이 끝날 때까지 기다리는 데 사용됩니다. 예이며 사용법을 자세히 알아볼 필요가 없습니다. 며칠 전, 코루틴에서 Add()
함수를 실행할 수 있는지 궁금했습니다. 대답은 '아니오'입니다. sync.WaitGroup
是并发环境中,一个相当常用的数据结构,用来等待所有协程的结束,在写代码的时候都是按着例子的样子写的,也没用深究过它的使用。前几日想着能不能在协程中执行Add()
函数,答案是不能,这里介绍下。
陷阱在WaitGroup的3个函数的调用顺序上。先回顾下3个函数的功能:
-
Add(delta int)
:给计数器增加delta,比如启动1个协程就增加1。 -
Done()
:协程退出前执行,把计数器减1。 -
Wait()
:阻塞等待计数器为0。
考一考
下面的程序是创建了协程father,然后father协程创建了10个子协程,main函数等待所有协程结束后退出,看看下面代码有没有什么问题?
package main import ( "fmt" "sync" ) func father(wg *sync.WaitGroup) { wg.Add(1) defer wg.Done() fmt.Printf("father\n") for i := 0; i < 10; i++ { go child(wg, i) } } func child(wg *sync.WaitGroup, id int) { wg.Add(1) defer wg.Done() fmt.Printf("child [%d]\n", id) } func main() { var wg sync.WaitGroup go father(&wg) wg.Wait() log.Printf("main: father and all chindren exit") }
发现问题了吗?如果没有看下面的运行结果:main函数在子协程结束前就开始结束了。
father main: father and all chindren exit child [9] child [0] child [4] child [7] child [8]
陷阱分析
产生以上问题的原因在于,创建协程后在协程内才执行Add()
函数,而此时Wait()
函数可能已经在执行,甚至Wait()
函数在所有Add()
执行前就执行了,Wait()
-
Add(delta int)
: 카운터에 델타를 추가합니다. 예를 들어 코루틴을 시작할 때 1씩 증가시킵니다.
-
Done()
: 코루틴이 종료되기 전에 실행되어 카운터를 1씩 감소시킵니다.
-
Wait()
: 차단 대기 카운터는 0입니다.
테스트해 보세요Add(delta int)
: 카운터에 델타를 추가합니다. 예를 들어 코루틴을 시작할 때 1씩 증가시킵니다. Done()
: 코루틴이 종료되기 전에 실행되어 카운터를 1씩 감소시킵니다. Wait()
: 차단 대기 카운터는 0입니다. 다음 프로그램은 코루틴 아버지를 생성하고, 아버지 코루틴은 10개의 하위 코루틴을 생성한 후 모든 코루틴이 완료될 때까지 기다린 후 코드를 확인합니다. 아래. 문제 없나요?
package main import ( "fmt" "sync" ) func father(wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("father\n") for i := 0; i < 10; i++ { wg.Add(1) go child(wg, i) } } func child(wg *sync.WaitGroup, id int) { defer wg.Done() fmt.Printf("child [%d]\n", id) } func main() { var wg sync.WaitGroup wg.Add(1) go father(&wg) wg.Wait() fmt.Println("main: father and all chindren exit") }
father child [9] child [7] child [8] child [1] child [4] child [5] child [2] child [6] child [0] child [3] main: father and all chindren exit
Add()
함수가 실행되는데 이때 Wait() 함수 <p>Maybe</p>가 이미 실행 중이거나 모든 <code>Add()
가 실행되기 전에 Wait()
함수도 실행되고 Wait가 실행되는 즉시 ()
가 실행됩니다. WaitGroup의 카운터가 0이 되면 Wait가 종료되고 메인 프로그램이 종료됩니다. 결과적으로 모든 하위 코루틴이 완전히 종료되지 않고 메인 기능이 종료됩니다.