防止main()函数在goroutines在Golang

时间:2017-03-12 20:28:39

标签: go concurrency main 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++
    }
}

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

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

4 个答案:

答案 0 :(得分:10)

最简单,最干净,最可扩展的&#34;这样做的方法是使用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()
}

输出(在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

简单&#34;规则&#34;使用sync.WaitGroup执行此操作时要遵循:

  • 在&#34;原文&#34;中拨打WaitGroup.Add()go陈述
  • 之前的goroutine(开始一个新的)
  • 建议致电WaitGroup.Done()延期,即使goroutine恐慌也会被调用
  • 如果要将WaitGroup传递给其他函数(而不是使用全局变量),则必须传递指向它的指针,否则将复制WaitGroup(这是一个结构),并且在原始
  • 上不会观察到副本上调用的Done()方法

答案 1 :(得分:3)

如前所述,sync.WaitGroup是生产代码中的正确方法。但在开发用于测试和调试目的时,您只需在末尾添加select{}语句或main()

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

main()然后永远不会返回,你会用例如Ctrl-C来杀死它。它不是惯用的,从未用于生产,而是在开发时非常快速的黑客攻击。

答案 2 :(得分:1)

如果您只想玩结果,可以使用&#34; hack&#34;等待输入:

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')
}

如果想了解如何进行同步阅读有关同步包的信息:

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()
}

答案 3 :(得分:1)

您可以使用sync package并查看waitgroups。您可以查看有效的Goplayground I set up.

基本上

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()
}