简体   繁体   English

有时我会看到在 goroutine 结束时没有调用 wg.Done() 的代码

[英]Sometimes I see code where wg.Done() is not being called in the end of the goroutine

I am new to the Golang, please bear me and kindly clarify my query.我是 Golang 的新手,请多多包涵并澄清我的疑问。

what I understood from wg.Done() call, this is an indication to WaitGroip that my goroutine is done so that wait call by WaitGroup will end, let's consider a scenario where we have only 1 goroutine running, So generally we use defer keyword for wg.Done() so it will get called at the end of the goroutine block.我从 wg.Done() 调用中了解到,这向 WaitGroip 表明我的 goroutine 已完成,因此 WaitGroup 的等待调用将结束,让我们考虑一个只有 1 个 goroutine 运行的场景,所以通常我们使用 defer 关键字wg.Done() 所以它将在 goroutine 块的末尾被调用。

I came across below code snippet, where wg.Done() it's being called in the midway, I am confused why it's being called here and also when I practiced code using wg.Done() in the midway, code after that it's not been called, So with that in mind I am confused in the below codebase wg.Done() is being called in the mid-way.我遇到了下面的代码片段,其中 wg.Done() 它在中途被调用,我很困惑为什么在这里调用它以及当我在中途使用 wg.Done() 练习代码时,之后的代码没有被调用调用,所以考虑到这一点,我对下面的代码库感到困惑 wg.Done() 在中途被调用。

func startNetworkDaemon() *sync.WaitGroup {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        connPool := warmServiceConnCache()

        server, err := net.Listen("tcp", "localhost:8080")
        if err != nil {
            log.Fatalf("cannot listen: %v", err)
        }
        defer server.Close()

        wg.Done()

        for {
            conn, err := server.Accept()
            if err != nil {
                log.Printf("cannot accept connection: %v", err)
                continue
            }
            svcConn := connPool.Get()
            fmt.Fprintln(conn, "")
            connPool.Put(svcConn)
            conn.Close()
        }
    }()
    return &wg
}

Another code sample as well, in the below code if add defer keyword to the wg.Done() method, I am getting a deadlock error.另一个代码示例,在下面的代码中,如果在 wg.Done() 方法中添加 defer 关键字,我会遇到死锁错误。 Can someone explain the reason for this...有人能解释一下这是什么原因吗...

func usingBroadcast() {

  beeper := sync.NewCond(&sync.Mutex{})
  var wg sync.WaitGroup
  wg.Add(1)

  miniFunc(func() {
    fmt.Println("mini1")  
    wg.Done() 
  }, beeper) 

  beeper.Broadcast() 
  wg.Wait()
  
}

func miniFunc(fn func(), beeper *sync.Cond) {

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
      wg.Done()
      beeper.L.Lock()
      fmt.Println("i am waiting")
      beeper.Wait()
      beeper.L.Unlock()
      fn()
      
    }()

    wg.Wait()
}

A waitgroup waits until all that are added into the group are done.等待组一直等待,直到添加到组中的所有内容都完成。 Those things in the waitgroup can be goroutines, or something else.等待组中的那些东西可以是 goroutine 或其他东西。 In the first code snippet, it looks like the waitgroup is there to capture the state where the goroutine completed initialization.在第一个代码片段中,等待组似乎在那里捕获了 goroutine 完成初始化的 state。 Once the goroutine calls the net.Listen, it notifies the waiting goroutine that the initialization is complete.一旦 goroutine 调用 net.Listen,它就会通知等待的 goroutine 初始化完成。

In the second example, there is a potential wait, and the waitgroup is notified before the wait.在第二个示例中,存在潜在的等待,等待组在等待之前得到通知。 It looks like a scheme to make sure that before the waitgroup is notified the goroutine started running.它看起来像是一个确保在通知等待组之前 goroutine 开始运行的方案。

The wait group is not needed in the examples.示例中不需要等待组。

If you look at the caller of startNetworkDaemon, you will find that it immediately calls Wait on the returned wait group.如果你查看 startNetworkDaemon 的调用者,你会发现它立即在返回的等待组上调用 Wait。 The caller is basically waiting on the creation on of the listener.调用者基本上是在等待监听器的创建。 Here's a refactoring that accomplishes the same goal:这是实现相同目标的重构:

func startNetworkDaemon() {
    connPool := warmServiceConnCache()

    server, err := net.Listen("tcp", "localhost:8080")
    if err != nil {
        log.Fatalf("cannot listen: %v", err)
    }

    go func() {
        defer server.Close()
        for {
            conn, err := server.Accept()
            if err != nil {
                log.Printf("cannot accept connection: %v", err)
                continue
            }
            svcConn := connPool.Get()
            fmt.Fprintln(conn, "")
            connPool.Put(svcConn)
            conn.Close()
        }
    }()
}

Because goroutine does not do anything before calling Done in the miniFunc example, the wait group serves no purpose.因为 goroutine 在miniFunc示例中调用 Done 之前不做任何事情,所以等待组没有任何作用。 Refactor by removing the wait group.通过删除等待组进行重构。

func miniFunc(fn func(), beeper *sync.Cond) {
    go func() {
      beeper.L.Lock()
      fmt.Println("i am waiting")
      beeper.Wait()
      beeper.L.Unlock()
      fn()
    }()
}

( not a direct answer to the question, more of a formatted comment ) 不是对问题的直接回答,更多的是格式化评论

The second example shows an incorrect usage of sync operations.第二个示例显示了同步操作的错误使用。 the shown example may deadlock (click several times on the [Run] button).所示示例可能会死锁(在[Run]按钮上单击几次)。

One way to avoid deadlock is to move the wg.Wait() operation in miniFunc , and have a way for the external caller to make sure that miniFunc() is in .Wait ing state as in this snippet :避免死锁的一种方法是在miniFunc中移动wg.Wait()操作,并让外部调用者确保miniFunc()在 .Wait .Wait中,如以下代码段所示

func usingBroadcast() {

    beeper := sync.NewCond(&sync.Mutex{})
    var wg sync.WaitGroup
    wg.Add(1)

    miniFunc(func() {
        fmt.Println("mini1")
        wg.Done()
    }, beeper)

    beeper.L.Lock() // call Lock / Unlock to make sure that the goroutine is in the '.Wait'ing state
    beeper.L.Unlock()

    beeper.Broadcast()
    wg.Wait()

}

func miniFunc(fn func(), beeper *sync.Cond) {

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        beeper.L.Lock()
        wg.Done() // move wg.Done() after beeper.L.Lock()
        fmt.Println("i am waiting")
        beeper.Wait()
        beeper.L.Unlock()
        fn()

    }()

    wg.Wait()
}

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

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