[英]Deferred call to sync.WaitGroup.Wait() in Goroutine: why should this work?
I'm trying to understand the Attack()
function ( https://github.com/tsenart/vegeta/blob/44a49c878dd6f28f04b9b5ce5751490b0dce1e18/lib/attack.go#L253-L312 ) in the source code of the vegeta
load testing tool/library . 我創建了一個簡化的示例:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go attack(&wg)
}
// wg.Wait()
go func() {
defer wg.Wait()
}()
}
func attack(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(1 * time.Second)
fmt.Println("foobar")
}
我注意到的是,這個 function 立即返回而不打印foobar
10 次。 只有在wg.Wait()
行中的注釋我才看到foobar
在 1 秒后打印了 10 次。 這對我來說很有意義,因為main()
function 在wg.Wait()
之前返回。
那么,我不明白Attack()
方法在vegeta
中是如何工作的,因為它似乎遵循類似的模式:
func (a *Attacker) Attack(tr Targeter, p Pacer, du time.Duration, name string) <-chan *Result {
var wg sync.WaitGroup
workers := a.workers
if workers > a.maxWorkers {
workers = a.maxWorkers
}
results := make(chan *Result)
ticks := make(chan struct{})
for i := uint64(0); i < workers; i++ {
wg.Add(1)
go a.attack(tr, name, &wg, ticks, results)
}
go func() {
defer close(results)
defer wg.Wait()
defer close(ticks)
began, count := time.Now(), uint64(0)
for {
elapsed := time.Since(began)
if du > 0 && elapsed > du {
return
}
wait, stop := p.Pace(elapsed, count)
if stop {
return
}
time.Sleep(wait)
if workers < a.maxWorkers {
select {
case ticks <- struct{}{}:
count++
continue
case <-a.stopch:
return
default:
// all workers are blocked. start one more and try again
workers++
wg.Add(1)
go a.attack(tr, name, &wg, ticks, results)
}
}
select {
case ticks <- struct{}{}:
count++
case <-a.stopch:
return
}
}
}()
return results
}
attack()
方法讀取的位置
func (a *Attacker) attack(tr Targeter, name string, workers *sync.WaitGroup, ticks <-chan struct{}, results chan<- *Result) {
defer workers.Done()
for range ticks {
results <- a.hit(tr, name)
}
}
我不明白為什么Attack()
function 在不調用attack()
的情況下不會立即返回,因為它的wg.Wait()
在 Goroutine 中?
vegeta 的Attack
也立即返回,但有一個由仍在運行的 goroutines 填充的通道。 一旦這些完成,通道就會關閉( defer close(results)
)啟用有result
的代碼來檢測完成。
例子;
package main
import (
"fmt"
"sync"
"time"
)
func main() {
results := attacks()
fmt.Println("attacks returned")
for result := range results {
fmt.Println(result)
}
}
func attacks() chan string {
// A channel to hold the results
c := make(chan string)
// Fire 10 routines populating the channel
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
attack(c)
wg.Done()
}()
}
// Close channel once routines are finished
go func() {
wg.Wait()
close(c)
}()
//
return c
}
func attack(c chan<- string) {
time.Sleep(1 * time.Second)
c <- "foobar"
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.