为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
至尊宝的传说 2022-10-04 15:51:01
我正在测试,如果我把函数的开头,像这样:sync.WaitGroupdefer wg.Done()package mainimport (        "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 functionfunc 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但是如果我把函数的末尾放进去,它就能成功运行,为什么呢?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}
查看完整描述

2 回答

?
智慧大石

TA贡献1946条经验 获得超3个赞

文档指出,“如果重用 a 来等待多组独立的事件,则必须在所有以前的 Wait 调用返回后进行新的 Add 调用”WaitGroup


在呼叫其他 goroutine 之前,您正在呼叫某些 goroutine,这是不允许的,如文档所述。在你开始所有这些戈鲁廷之前,你需要打电话,你还不如叫一次,wg.Done()wg.Add(1)wg.Addwg.Add(1000)


你的其他代码工作的原因是它从不调用,你有wg.Done()


if true {

     return nil

}

defer wg.Done()

因此,您始终返回而不到达 defer 语句,因此永远不会对 进行任何调用。wg.Done()


请执行下列操作:


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

}


查看完整回答
反对 回复 2022-10-04
?
12345678_0001

TA贡献1802条经验 获得超5个赞

问题是,如果在等待组上调用,则不允许重用此等待组并再次调用它,直到返回调用(请参阅文档)。在此程序中,调用工作函数本身就是一个 go 例程,所有调用工作函数都同时运行。在没有等待对方的情况下,他们试图在上一个电话未完成时打电话。Wait()Add()Wait()Add()Wait()


第一个工作线程生成结果时没有错误,因为他们的调用在下一个调用(一个经典的竞争条件)之前幸运地返回。Wait()Add()


如果您希望工作线程同时运行,则必须移动并退出调用工作器函数。 必须在 for 循环之后调用。并且应该在调用工作之前在循环内调用,否则程序将在callWorker有机会向等待组添加某些内容之前完成,因此无需等待。不需要时间。睡在里面,太。Wait()Add()Wait()Add()Wait()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")

}


查看完整回答
反对 回复 2022-10-04
  • 2 回答
  • 0 关注
  • 66 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信