防止main()函数在goroutines在Golang中完成之前终止

luq*_*o33 10 concurrency program-entry-point go goroutine

已经厌倦了这个人为的例子:

package main

import "fmt"

func printElo() {
    fmt.Printf("Elo\n")
}

func printHello() {
    fmt.Printf("Hello\n")
}

func main() {
    fmt.Printf("This will print.")
    i := 0
    for i < 10 {
        go printElo()
        go printHello()
        i++
    }
}
Run Code Online (Sandbox Code Playgroud)

这个程序的输出只是"这将打印".输出goroutine printElo()并且printHello不会被发出因为,我猜,main()函数线程将在goroutines甚至有机会开始执行之前完成.

在Golang中使类似代码工作而不是过早终止的惯用方法是什么?

icz*_*cza 14

最简单,最清洁和"可扩展"的方法是使用sync.WaitGroup:

var wg = &sync.WaitGroup{}

func printElo() {
    defer wg.Done()
    fmt.Printf("Elo\n")
}

func printHello() {
    defer wg.Done()
    fmt.Printf("Hello\n")
}

func main() {
    fmt.Printf("This will print.")
    i := 0
    for i < 10 {
        wg.Add(1)
        go printElo()
        wg.Add(1)
        go printHello()
        i++
    }
    wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

This will print.Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Run Code Online (Sandbox Code Playgroud)

在执行此操作时要遵循的简单"规则" sync.WaitGroup:

  • WaitGroup.Add()go声明之前调用"原始"goroutine(开始一个新的)
  • 建议调用WaitGroup.Done()deferred,因此即使goroutine发生恐慌也会调用它
  • 如果你想传递WaitGroup给其他函数(而不是使用全局变量),你必须传递一个指向它的指针,否则WaitGroup将复制(这是一个结构),并且Done()不会在上面调用复制上调用的方法.原本的

  • @AndreaM16 为简单起见,我在这里使用了全局变量。如果您的代码/包的复杂性增加,或者您同时使用多个等待组,那么全局变量可能不可行或可能使事情变得比应有的更复杂。请参阅编辑后的答案。 (2认同)