不同的工作组Done() 导致在上一个等待返回之前重用等待组

我正在测试,如果我把函数的开头,像这样:sync.WaitGroupdefer wg.Done()


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

}

在某些情况下,我会得到,像这样WaitGroup is reused before previous Wait has returned

http://img4.mukewang.com/633be60a0001dabd09380558.jpg

但是如果我把函数的末尾放进去,它就能成功运行,为什么呢?defer wg.Done()


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

}


至尊宝的传说
浏览 81回答 2
2回答

智慧大石

文档指出,“如果重用 a 来等待多组独立的事件,则必须在所有以前的 Wait 调用返回后进行新的 Add 调用”WaitGroup在呼叫其他 goroutine 之前,您正在呼叫某些 goroutine,这是不允许的,如文档所述。在你开始所有这些戈鲁廷之前,你需要打电话,你还不如叫一次,wg.Done()wg.Add(1)wg.Addwg.Add(1000)你的其他代码工作的原因是它从不调用,你有wg.Done()if true {&nbsp; &nbsp; &nbsp;return nil}defer wg.Done()因此,您始终返回而不到达 defer 语句,因此永远不会对 进行任何调用。wg.Done()请执行下列操作:func callWorker(i int){&nbsp; &nbsp; fmt.Println("Main: Starting worker", i)&nbsp; &nbsp; // you cannot call Add here because Done has been called in other goroutines&nbsp; &nbsp; go worker(&wg, i)&nbsp; &nbsp; wg.Wait()}func main() {&nbsp; &nbsp; wg.Add(1000) // <---- You must call Add before Done is called in any goroutine&nbsp; &nbsp; for i := 0; i < 1000; i++ {&nbsp; &nbsp; &nbsp; &nbsp; go callWorker(i)&nbsp; &nbsp; }&nbsp; &nbsp; time.Sleep(time.Second * 60)&nbsp; &nbsp; fmt.Println("Main: Completed")}

12345678_0001

问题是,如果在等待组上调用,则不允许重用此等待组并再次调用它,直到返回调用(请参阅文档)。在此程序中,调用工作函数本身就是一个 go 例程,所有调用工作函数都同时运行。在没有等待对方的情况下,他们试图在上一个电话未完成时打电话。Wait()Add()Wait()Add()Wait()第一个工作线程生成结果时没有错误,因为他们的调用在下一个调用(一个经典的竞争条件)之前幸运地返回。Wait()Add()如果您希望工作线程同时运行,则必须移动并退出调用工作器函数。 必须在 for 循环之后调用。并且应该在调用工作之前在循环内调用,否则程序将在callWorker有机会向等待组添加某些内容之前完成,因此无需等待。不需要时间。睡在里面,太。Wait()Add()Wait()Add()Wait()main()func main() {&nbsp; &nbsp; for i := 0; i < 1000; i++ {&nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; go callWorker(i)&nbsp; &nbsp; }&nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; fmt.Println("Main: Waiting for workers to finish")&nbsp; &nbsp; fmt.Println("Main: Completed")}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go