douba9425
2017-03-12 20:28
采纳率: 100%
浏览 220
已采纳

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

Have loook at this contrived example:

package main

import "fmt"

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

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

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.

What is the idiomatic way to make similar code work in Golang and not terminate prematurely?

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

4条回答 默认 最新

  • dousuitang5239 2017-03-12 20:51
    已采纳

    Simplest, cleanest and "scalable" way to do it is to use a sync.WaitGroup:

    var wg = &sync.WaitGroup{}
    
    func printElo() {
        defer wg.Done()
        fmt.Printf("Elo
    ")
    }
    
    func printHello() {
        defer wg.Done()
        fmt.Printf("Hello
    ")
    }
    
    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):

    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:

    • call WaitGroup.Add() in the "original" goroutine (that starts a new) before the go statement
    • recommended to call WaitGroup.Done() deferred, so it gets called even if the goroutine panics
    • if you want to pass WaitGroup to other functions (and not use a global 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
    已采纳该答案
    评论
    解决 无用
    打赏 举报
  • dongtanghuan1885 2017-03-12 20:54

    If you want just to play with results you can use "hack" with waiting for input:

    package main
    
    import (
        "fmt"
        "bufio"
        "os"
    )
    
    func printElo() {
        fmt.Printf("Elo
    ")
    }
    
    func printHello() {
        fmt.Printf("Hello
    ")
    }
    
    func main() {
        fmt.Printf("This will print.")
        i := 0
        for i < 10 {
            go printElo()
            go printHello()
            i++
        }
    
        reader := bufio.NewReader(os.Stdin)
        reader.ReadString('
    ')
    }
    

    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
    ")
        wg.Done()
    }
    
    func printHello() {
        fmt.Printf("Hello
    ")
        wg.Done()
    }
    
    func main() {
    
        fmt.Printf("This will print.")
        i := 0
        for i < 10 {
            wg.Add(2)
            go printElo()
            go printHello()
            i++
        }
    
        wg.Wait()
    }
    
    评论
    解决 无用
    打赏 举报
  • dtuct88226 2017-03-12 20:54

    You can use sync package and take a look at waitgroups. You can take a look at a working Goplayground I set up.

    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
    ")
        defer wg.Done()
    }
    
    //Takes a reference to the wg and sleeps when work is done
    func printHello(wg *sync.WaitGroup) {
        fmt.Printf("Hello
    ")
        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()
    }
    
    评论
    解决 无用
    打赏 举报
  • dqjjw04440 2017-03-13 10:52

    As already mentioned sync.WaitGroup is a right way in production code. But when developing for test and debug purposes you can just add select{} statement at the end or the main().

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

    main() then never returns and you would kill it with for example Ctrl-C. It isn't idiomatic, never used in production, but just very quick easy hack when developing.

    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题