簡體   English   中英

在 Goroutine 中延遲調用 sync.WaitGroup.Wait():為什么要這樣做?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM