📜  在 Golang 中使用 WaitGroup

📅  最后修改于: 2021-10-24 14:03:51             🧑  作者: Mango

Go 例程是 golang 的一个很好的卖点,使其成为许多开发人员的选择。在这篇文章中,我们将看到这些goroutine 的一个常见问题并尝试解决它。

让我们看一个简单的代码片段来说明这个问题,

Go
package main
  
import "fmt"
  
func runner1() {
    fmt.Print("\nI am first runner")
}
  
func runner2() {
    fmt.Print("\nI am second runner")
}
  
func execute() {
    go runner1()
    go runner2()
  
}
  
func main() {
    
    // Launching both the runners
    execute()
}


Go
package main
  
import (
    "fmt"
    "time"
)
  
func runner1() {
    fmt.Print("\nI am first runner")
}
  
func runner2() {
    fmt.Print("\nI am second runner")
}
  
func execute() {
    go runner1()
    go runner2()
  
}
  
func main() {
  
    // Launching both the runners
    execute()
    time.Sleep(time.Second)
}


Go
package main
  
import (
    "fmt"
    "sync"
)
  
func runner1(wg *sync.WaitGroup) {
    defer wg.Done() // This decreases counter by 1
    fmt.Print("\nI am first runner")
  
}
  
func runner2(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Print("\nI am second runner")
}
  
func execute() {
    wg := new(sync.WaitGroup)
    wg.Add(2)
  
    // We are increasing the counter by 2
    // because we have 2 goroutines
    go runner1(wg)
    go runner2(wg)
  
    // This Blocks the execution
    // until its counter become 0
    wg.Wait()
}
  
func main() {
    // Launching both the runners
    execute()
}


正如您刚刚看到的那样,输出中没有任何内容,这是因为一旦您启动了两个 goroutine,您的 main函数终止。 Golang 中的每个程序都会执行,直到 main函数没有终止。那么,对于这个问题,我们能做些什么

1.我们可以在运行 runners 后等待一段时间,为此我们将使用“ time ”包函数“ Sleep ”在给定的时间内暂停函数的执行,

package main
  
import (
    "fmt"
    "time"
)
  
func runner1() {
    fmt.Print("\nI am first runner")
}
  
func runner2() {
    fmt.Print("\nI am second runner")
}
  
func execute() {
    go runner1()
    go runner2()
  
}
  
func main() {
  
    // Launching both the runners
    execute()
    time.Sleep(time.Second)
}

输出:

I am second runner
I am first runner

我们只是解决了这个问题,在启动我们的跑步者后我们等待一秒钟,所以我们的主要函数是睡眠(阻塞)1 秒。在这段时间内,所有的 go 例程都成功执行了。但是 Golang 是一种快速的语言,只需打印 2 个字符串就不需要 1 秒。

问题是,我们的执行程序执行时间很短,因此我们不必要地将程序阻塞了 1 秒。在这个例子中,这似乎不是一个关键问题,但如果你制作一个生产级服务器,它将同时为 1000 个请求提供服务,这将是一个大问题。

2.让我们使用另一个 Golang 的标准库原语“ sync.WaitGroup ”。 WaitGroup实际上是一种计数器,它阻止函数的执行(或者可以说是一个 goroutine),直到其内部计数器变为 0

这个怎么运作 ?

WaitGroup 导出 3 个方法。

1 Add(int)  It increases WaitGroup counter by given integer value.
2 Done()  It decreases WaitGroup counter by 1, we will use it to indicate termination of a goroutine.
3 Wait() It Blocks the execution until it’s internal counter becomes 0.

注意:WaitGroup 是并发安全的,因此将指向它的指针作为 Groutine 的参数传递是安全的。

package main
  
import (
    "fmt"
    "sync"
)
  
func runner1(wg *sync.WaitGroup) {
    defer wg.Done() // This decreases counter by 1
    fmt.Print("\nI am first runner")
  
}
  
func runner2(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Print("\nI am second runner")
}
  
func execute() {
    wg := new(sync.WaitGroup)
    wg.Add(2)
  
    // We are increasing the counter by 2
    // because we have 2 goroutines
    go runner1(wg)
    go runner2(wg)
  
    // This Blocks the execution
    // until its counter become 0
    wg.Wait()
}
  
func main() {
    // Launching both the runners
    execute()
}

输出:

I am second runner
I am first runner

输出相同,但我们的程序不会阻塞 1 秒。模式,我们上面向您展示的是在 Golang 中编写并发代码的常见做法。