简体   繁体   English

不同的 wg.Done() 导致 WaitGroup 在先前的 Wait 返回之前被重用

[英]Different wg.Done() causes WaitGroup is reused before previous Wait has returned

I am testing sync.WaitGroup , if I put defer wg.Done() in the begining of the function, like this:我正在测试sync.WaitGroup ,如果我将defer wg.Done()放在 function 的开头,如下所示:

package main

import (
        "fmt"
        "sync"
        "time"
)

func worker(wg *sync.WaitGroup, id int) error {
    defer wg.Done() // put here cause error
    fmt.Printf("Worker %v: Finished\n", id)
    if true {
        return nil
    }

    return nil
}

var wg sync.WaitGroup // I should put `wg` outside of this function
func callWorker(i int){
    fmt.Println("Main: Starting worker", i)
    fmt.Printf("Worker %v: Finished\n", id)
    wg.Add(1)
    go worker(&wg, i)
    wg.Wait()
}

func main() {
    for i := 0; i < 1000; i++ {
        go callWorker(i)
    }
    time.Sleep(time.Second * 60)
    fmt.Println("Main: Waiting for workers to finish")

    fmt.Println("Main: Completed")
}

I will get WaitGroup is reused before previous Wait has returned in some cases, like this在某些情况下,我会WaitGroup is reused before previous Wait has returned ,就像这样

在此处输入图像描述

but if I put defer wg.Done() in the end of function, it runs successfully, why?但是如果我把defer wg.Done()放在 function 的末尾,它运行成功,为什么?

func worker(wg *sync.WaitGroup, id int) error {
    
    fmt.Printf("Worker %v: Finished\n", id)
    if true {
        return nil
    }
    defer wg.Done() // put here, it is ok
    return nil
}

The docs state that "If a WaitGroup is reused to wait for several independent sets of events, new Add calls must happen after all previous Wait calls have returned"文档指出“如果重用WaitGroup来等待多个独立的事件集,则必须在所有先前的 Wait 调用返回后进行新的 Add 调用”

You are calling wg.Done() in some goroutines before calling wg.Add(1) in others, which is not allowed, as stated by the docs.您在某些 goroutine 中调用wg.Done()在其他 goroutine 中调用wg.Add(1) ,这是不允许的,如文档所述。 You need to call wg.Add before you start all those goroutines, and you might as well just call it once, wg.Add(1000)你需要在启动所有这些 goroutine 之前调用wg.Add ,你最好只调用一次wg.Add(1000)

The reason your other code works is that it never calls wg.Done() , you have你的其他代码工作的原因是它从不调用wg.Done() ,你有

if true {
     return nil
}
defer wg.Done()

so you always return without reaching the defer statement, so there are never any calls to wg.Done() .所以你总是在没有到达 defer 语句的情况下返回,所以永远不会有任何对wg.Done()调用。

Do this:做这个:

func callWorker(i int){
    fmt.Println("Main: Starting worker", i)
    // you cannot call Add here because Done has been called in other goroutines
    go worker(&wg, i)
    wg.Wait()
}

func main() {
    wg.Add(1000) // <---- You must call Add before Done is called in any goroutine
    for i := 0; i < 1000; i++ {
        go callWorker(i)
    }
    time.Sleep(time.Second * 60)
    fmt.Println("Main: Completed")
}

The problem is that if Wait() is called on a waitgroup, it is not allowed to reuse this waitgroup and call Add() on it again until the Wait() call is returned (see docs ).问题是,如果在等待组上调用Wait() ,则不允许重用此等待组并再次调用Add()直到返回Wait()调用(请参阅文档)。 In this programm the callWorker function is itself a go routine and all callWorker functions are running concurrently.在这个程序中,callWorker function 本身就是一个 go 例程,所有 callWorker 函数都同时运行。 Without waiting for each other they try to call Add() while a previous Wait() call isn't finished.在之前的Wait()调用未完成时,他们不等待对方尝试调用Add()

The first workers yield results without error because their Wait() call is luckily returned before the next Add() call, a classical race condition.第一个 worker 产生结果没有错误,因为他们的Wait()调用幸运地在下一个Add()调用之前返回,这是一个经典的竞争条件。

If you want the workers to run concurrently you have to move Wait() and Add() out of the callWorker function. Wait() must be called after the for-loop.如果您希望工作人员同时运行,则必须将Wait()Add()移出 callWorker function。必须在 for 循环之后调用Wait() And Add() should be called inside the loop before callWorker, otherwise the program will finish before callWorker has a chance to add something to the waitgroup and hence Wait() has nothing to wait for.并且Add()应该在 callWorker 之前在循环内调用,否则程序将在 callWorker 有机会向等待组添加内容之前完成,因此Wait()没有什么可等待的。 There is no need for time.Sleep in main() , too.也不需要 time.Sleep 中的main()

func main() {
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go callWorker(i)
    }
    wg.Wait()
    fmt.Println("Main: Waiting for workers to finish")

    fmt.Println("Main: Completed")
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM