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

前端 未结 4 967
悲哀的现实
悲哀的现实 2020-11-28 15:47

Have loook at this contrived example:

package main

import \"fmt\"

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

func printHello() {
    fmt.Printf(\"Hel         


        
相关标签:
4条回答
  • 2020-11-28 16:12

    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\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()
    }
    
    0 讨论(0)
  • 2020-11-28 16:19

    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\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):

    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
    0 讨论(0)
  • 2020-11-28 16:25

    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.

    0 讨论(0)
  • 2020-11-28 16:29

    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\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()
    }
    
    0 讨论(0)
提交回复
热议问题