BufBills
BufBills

Reputation: 8113

go lang, why go routine function never being called

package main

import (
    "fmt"
    //"runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        //runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say("world") // create a new goroutine
    say("hello") // current goroutine
}

Why result is:

hello
hello
hello
hello
hello

Why is there no world?

Answer: (edited:) If I do this, it is good now:

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        //runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say("world") // create a new goroutine
    runtime.Gosched()

    say("hello") // current goroutine
}

Upvotes: 1

Views: 1942

Answers (3)

xnslong
xnslong

Reputation: 1

It's because the main goroutine exits too early. When the main goroutine exits, the process will exit, so there is no chance for other goroutines to run. If you wish the goroutines to run, keep the main routine alive long enough.

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        fmt.Println(s)
    }
}

func main() {
    go say("world") // create a new goroutine
    runtime.Gosched()

    say("hello") // current goroutine
    time.Sleep(1 * time.Second) // this line
}

Upvotes: 0

evanmcdonnal
evanmcdonnal

Reputation: 48154

You're just experiencing a timing issue because you're not 'coordinating' your go routines. The common way to handle this is with a wait guard. The other option I see from time to time using channels and a blocking select. A wait guard implementation would look something like this;

func main() {
    wg := sync.WaitGroup{}
    wg.Add(1)
    go say("world") // create a new goroutine

    wg.Add(1)
    say("hello") // current goroutine
    wg.Wait()
}

While the channel option (not actually interesting or useful in this example) is something more like this;

 func main() {
    done := make(chan bool)
    go say("world", done) // create a new goroutine

    say("hello", done) // current goroutine

    select {
        case fin := <- done:
           //on finished
           return
    }
}

func say(s string, chan bool) {
for i := 0; i < 5; i++ {
    fmt.Println(s)
   }
   done <- true
}

With the example above though, the first call to say completing would allow the program to finish executing. To ensure both finish you'd have to pass different channels to each and have blocking reads on both. This is more a pattern I would use when your go routines are doing real work and you want to bring data to aggregate data from them or need more complex coordination (like spawning new goroutines based on the results of previous ones ect).

Upvotes: 3

Mr_Pink
Mr_Pink

Reputation: 109464

You're not allowing the goroutine to run before main() exits.

Even though the second call to say blocks (briefly), there's no guarantee that the first goroutine can run. You need to wait for both to return, which is often done with a WaitGroup

var wg sync.WaitGroup

func say(s string) {
    defer wg.Done()
    for i := 0; i < 5; i++ {
        //runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    wg.Add(1)
    go say("world") // create a new goroutine

    wg.Add(1)
    say("hello") // current goroutine
    wg.Wait()
}

Upvotes: 2

Related Questions