在Goroutine中推迟对sync.WaitGroup.Wait()的调用:为什么应该这样做?

我正在尝试了解Attack()负载测试工具/库的源代码中的vegeta函数(https://github.com/tsenart/vegeta/blob/44a49c878dd6f28f04b9b5ce5751490b0dce1e18/lib/attack.go#L253-L312)。我创建了一个简化的示例:

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")
}

我注意到的是,此函数立即返回而无需打印foobar 10次。只有看到wg.Wait()行中的注释,foobar在一秒钟后打印了10次。这对我来说很有意义,因为main()函数在调用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,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()函数不会在不调用attack()的情况下立即返回,因为它的wg.Wait()位于Goroutine中?

calary 回答:在Goroutine中推迟对sync.WaitGroup.Wait()的调用:为什么应该这样做?

vegeta的$(document).ready(function(){ $('.owl-carousel').owlCarousel({ loop:false,margin:1,nav:false,dots:true,responsive:{ 0:{ items:2 //I just change this },600:{ items:3 },1000:{ items:5 } } }); }); 也立即返回,但是具有一个通道,该通道由仍在运行的goroutine填充。 完成这些操作后,通道将关闭(Attack),使包含defer close(results)的代码可以检测到完成情况。

示例;

result
本文链接:https://www.f2er.com/3099204.html

大家都在问