简体   繁体   English

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

[英]Prevent the main() function from terminating before goroutines finish in Golang

Have loook at this contrived example:看看这个人为的例子:

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++
    }
}

The output of this program would be just "This will print".该程序的输出将只是“这将打印”。 Output of goroutines printElo() and printHello will not be emitted because, I guess, the main() function thread will finish before the goroutines have a chance to even start executing. goroutines printElo()printHello输出不会被发出,因为我猜main()函数线程会在 goroutines 有机会开始执行之前完成。

What is the idiomatic way to make similar code work in Golang and not terminate prematurely?使类似代码在 Golang 中工作并且不会过早终止的惯用方法是什么?

Simplest, cleanest and "scalable" way to do it is to use a sync.WaitGroup :最简单、最干净和“可扩展”的方法是使用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()
}

Output (try it on the Go Playground ):输出(在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

Simple "rules" to follow when doing it with sync.WaitGroup :使用sync.WaitGroup执行此操作时要遵循的简单“规则”:

  • call WaitGroup.Add() in the "original" goroutine (that starts a new) before the go statementgo语句之前在“原始”goroutine(启动一个新的)中调用WaitGroup.Add()
  • recommended to call WaitGroup.Done() deferred, so it gets called even if the goroutine panics建议延迟调用WaitGroup.Done() ,因此即使 goroutine WaitGroup.Done()被调用
  • if you want to pass WaitGroup to other functions (and not use a package level variable), you must pass a pointer to it, else the WaitGroup (which is a struct) would be copied, and the Done() method called on the copy wouldn't be observed on the original如果要将WaitGroup传递给其他函数(而不是使用包级别变量),则必须传递指向它的指针,否则将复制WaitGroup (结构体),并在副本上调用Done()方法不会在原版上观察到

As already mentioned sync.WaitGroup is a right way in production code.如前所述, sync.WaitGroup是生产代码中的正确方法。 But when developing for test and debug purposes you can just add select{} statement at the end or the main() .但是在为测试和调试目的进行开发时,您可以在末尾或main()添加select{}语句。

func main(){
    go routine()
    ...
    select{}
}

main() then never returns and you would kill it with for example Ctrl-C . main()然后永远不会返回,你会用例如Ctrl-C杀死它。 It isn't idiomatic, never used in production, but just very quick easy hack when developing.它不是惯用的,从未在生产中使用过,但只是在开发时非常快速容易破解。

You can use sync package and take a look at waitgroups .您可以使用同步包并查看waitgroups You can take a look at a working Goplayground I set up.你可以看看我设置的一个有效的Goplayground。

Essentially本质上

package main

import (
    "fmt"
    "sync"
    )

//Takes a reference to the wg and sleeps when work is done
func printElo(wg *sync.WaitGroup) {
    fmt.Printf("Elo\n")
    defer wg.Done()
}

//Takes a reference to the wg and sleeps when work is done
func printHello(wg *sync.WaitGroup) {
    fmt.Printf("Hello\n")
    defer wg.Done()
}

func main() {
    //Create a new WaitGroup
    var wg sync.WaitGroup
    fmt.Println("This will print.")

    for  i := 0; i < 10; i++ {
        //Add a new entry to the waitgroup
        wg.Add(1)
        //New Goroutine which takes a reference to the wg
        go printHello(&wg)
        //Add a new entry to the waitgroup
        wg.Add(1)
        //New Goroutine which takes a reference to the wg
        go printElo(&wg)
    }
    //Wait until everything is done
    wg.Wait()
}

If you want just to play with results you can use "hack" with waiting for input:如果你只想玩结果,你可以使用“hack”等待输入:

package main

import (
    "fmt"
    "bufio"
    "os"
)

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++
    }

    reader := bufio.NewReader(os.Stdin)
    reader.ReadString('\n')
}

If want to learn how to do synchronization read about sync package:如果想了解如何进行同步,请阅读有关同步包的信息:

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

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

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

func main() {

    fmt.Printf("This will print.")
    i := 0
    for i < 10 {
        wg.Add(2)
        go printElo()
        go printHello()
        i++
    }

    wg.Wait()
}

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

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