1

I've written this little program with go. About go keyword, I just know that when I call a function in this manner, is executed simultaenously. What I've tried to execute this code without time.Sleep(), but no output is generated. I need to add time.Sleep(1000) twice. One time.Sleep statement is not enough. Why?

package main

import (
    "fmt"
    "time"
)

func doSomething(integer int) {
    fmt.Println(integer)
}

func main() {
    i := 1

    for i <= 10 {
        go doSomething(i)
        i++
    }

    time.Sleep(1000)
    time.Sleep(1000)
}
sensorario
  • 20,262
  • 30
  • 97
  • 159
  • 3
    This is [a very good answer](http://stackoverflow.com/questions/18207772/how-to-wait-for-all-goroutines-to-finish-without-using-time-sleep) to essentially the same question as yours. – Greg C May 12 '16 at 20:32
  • If you are looking for a way to ensure that all goroutines finish before exiting main, [this might help](https://stackoverflow.com/questions/16228887/why-does-fmt-println-inside-a-goroutine-not-print-a-line). Just from a curiosity standpoint though, I'll have to do more research. How do you execute the code, from the command line or an IDE? Does the window close as soon as main ends? – Frank Bryce May 12 '16 at 20:33
  • From command line. – sensorario May 12 '16 at 20:35
  • 1
    @JohnCarpenter just run his example in the go playground. Execution completes as soon as the goroutines are kicked off because there are no further statements. It doesn't actually matter how you run it. If it were an exe and you double clicked it the window would close, if you instead opened a command prompt and invoked the exe, the window would stay open and just move onto the next prompt. – evanmcdonnal May 12 '16 at 20:36
  • 1
    because scheduling the goroutine and calling `fmt.Println(integer)` takes more than 1 microsecond. – JimB May 12 '16 at 20:39
  • @JimB, can go close program only when all goroutines have completed their execution? – sensorario May 12 '16 at 20:41
  • @sensorario no, as you've observed it exists instantly. In realistic programs where the whole thing doesn't shut down at that time you would 'leak goroutines' which has a similar effect to having a memory leak in C++ – evanmcdonnal May 12 '16 at 20:42
  • 1
    @sensorario: yes, but it's up to you to coordinate that yourself, either with channels or a sync.WaitGroup – JimB May 12 '16 at 20:42
  • 1
    Please condier reading a book first ([this one](https://golang.org/doc/effective_go.html#concurrency) will be a good start, and see [this](https://github.com/golang/go/wiki/Books) for a list). Trying out things is fine but doing this wihtout any foundation knowledge of the subject is mostly wasting time -- both yours and ours. – kostix May 13 '16 at 10:35
  • @kostix thanks. I've read an introducing book, and i am reading another one. Thank you for your links. – sensorario May 15 '16 at 17:46

2 Answers2

4

That's because when you call a function with go (ie as a goroutine) it is run in the background and execution continues. In this case, you have nothing for the program to do after invoking the goroutine so it just completes and you see no output. Why do you need two? Well you don't, it's just the 1 second sleep wasn't long enough, if you had time.Sleep(2000) you'd probably get the same result (though it's not guaranteed). Generally when using a goroutine, you need some kind of blocking statement some where. The simplest way to do this, imo, is to pass a channel to the goroutine and then receive from it.

This isn't the most useful example due to the structuring of your code, but here's a modified version of your program that will block until the first goroutine completes. Since you're actually invoking 10 goroutines, you would need a bit more robust logic to block until of them complete but a more reasonable solution would be to put the loop in the gouroutine, rather than invoking 10 goroutines. https://play.golang.org/p/66g9u8Bhjj

Also, check out this more realistic example in the go tour; https://tour.golang.org/concurrency/2

evanmcdonnal
  • 46,131
  • 16
  • 104
  • 115
  • time.Sleep(10000) isnt enough. but program does not wait 10 seconds. – sensorario May 12 '16 at 20:32
  • @sensorario no time is assured to work, that's why you should use channels. The goroutine should tell the caller that it has completed rather than relying on coincidence. – evanmcdonnal May 12 '16 at 20:34
  • 7
    @sensorario: `time.Sleep(10000)` is 10 *microseconds*. Use the constants defined in the time package, `time.Sleep(10*time.Second)` – JimB May 12 '16 at 20:37
  • I'd love to see the go runtime thread handling code. Does anyone know if go is open source? – Frank Bryce May 12 '16 at 20:39
  • 2
    @JohnCarpenter: not a hard question to answer: https://golang.org "Go is an open source programming language..." – JimB May 12 '16 at 20:40
  • 1
    @JohnCarpenter, consider reading [this document](https://morsmachine.dk/go-scheduler) -- it explains how Go runtime implements M×N scheduling of goroutines onto OS threads. – kostix May 13 '16 at 10:32
1

try this:

package main

import (
    "fmt"
    "runtime"
    "time"
)

func doSomething(integer int) {
    fmt.Println(integer)
}

func main() {
    i := 1

    for i <= 10 {
        go doSomething(i)
        i++
    }
    runtime.Gosched()
    time.Sleep(10 * time.Millisecond)
}

It will runs differently.

Do you know how much time it takes to finish this (your) code:
(Consider CPU Loaded 100% with other process with higher priority.)

func doSomething(integer int) {
    fmt.Println(integer)
}
for i <= 10 {
    go doSomething(i)
    i++
}

Simple answer: no.
It depends to many unknown factors on each system:
1- CPU speed and number of CPU cores and CPU caches...
2- RAM speed and bus speed and bus matrix (is it concurrent?)
2- OS scheduler and OS Load and priority
3- Golang scheduler
4- CPU Load
...

What I mean is: In concurrent systems and concurrent programming it is not good idea to rely on simple code (or assembly) instruction timing.

Yes it is good to know Golang scheduler and how it works, but Even you know how Golang scheduler behaves, do you know what OS doing now?

Only in real-time OS it is estimated to some known time in the ranges like milliseconds or so.

even sometimes it is better to limit concurrency:
this is one way to limit the concurrency:

//using chan to limit the concurrency
package main

import (
    "os/exec"
    "runtime"
    "sync"
)

func main() {
    numberOfJobs := 10
    c := make(chan *exec.Cmd, numberOfJobs)
    for i := 0; i < numberOfJobs; i++ {
        c <- exec.Command("notepad")
    }
    close(c)

    nCPU := runtime.NumCPU()
    var wg sync.WaitGroup
    for i := 0; i < nCPU; i++ {
        wg.Add(1)
        go func() {
            for cmd := range c {
                cmd.Run()
            }
            wg.Done()
        }()
    }

    wg.Wait()
}

this is another way to limit the concurrency:

//using chan to limit the concurrency
package main

import (
    "os/exec"
    "runtime"
    "sync"
)

func main() {
    n := runtime.NumCPU()
    dummy := make(chan bool, n)
    numberOfJobs := 10
    var wg sync.WaitGroup
    for i := 0; i < numberOfJobs; i++ {
        cmd := exec.Command("notepad")
        dummy <- true // wait here if no CPU available
        wg.Add(1)
        go func(cmd *exec.Cmd) {
            //defer func() { <-dummy }()
            cmd.Run()
            wg.Done()
            <-dummy
        }(cmd)
    }
    close(dummy)

    wg.Wait()
}

I hope this helps.