登录
首页 >  Golang >  Go问答

在 Goroutine 中延迟调用sync.WaitGroup.Wait():为什么这会起作用?

来源:stackoverflow

时间:2024-04-08 09:24:34 265浏览 收藏

本篇文章给大家分享《在 Goroutine 中延迟调用sync.WaitGroup.Wait():为什么这会起作用?》,覆盖了Golang的常见基础知识,其实一个语言的全部知识点一篇文章是不可能说完的,但希望通过这些问题,让读者对自己的掌握程度有一定的认识(B 数),从而弥补自己的不足,更好的掌握它。

问题内容

我正在尝试理解attack()函数(https://github.com.com/tsenart/veta/vegeta/blob/4449c878dd6f26f28f04b9b9b9b9b9b9b9b9b9b90b51490b0b0b0b0b queb qus of solur qbendczqb负载测试工具/库。我创建了一个简化的示例:

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 在 1 秒后打印 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, 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() 函数在不调用 attack() 的情况下不会立即返回,因为它的 wg.wait() 位于 goroutine 内部?


解决方案


vegeta 的 attack 也立即返回,但通道中填充了仍在运行的 goroutine。 一旦这些完成,通道就会关闭(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"
}

今天关于《在 Goroutine 中延迟调用sync.WaitGroup.Wait():为什么这会起作用?》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>