1

I don't understand why Goroutine in Windows didn't finish properly like Goroutine in Linux?
I already run the code in Powershell, VSCode, Goland, and even CMD, but the code never finish properly like Linux output.

below is the code:

import (
    "fmt"
    "time"
)

func count() {
    for i := 0; i < 5; i++ {
        fmt.Println(i)
        time.Sleep(time.Millisecond * 1)
    }
}

func main() {
    go count()
    time.Sleep(time.Millisecond * 2)
    fmt.Println("Hello World")
    time.Sleep(time.Millisecond * 5)
}

Windows Output:

0
1
Hello World

Linux Output (Which is expected output):

0
1
2
Hello World
3
4

Kindly help me understand or how to fix this.

p/s: I just started learning Go.

1

2 Answers 2

2

You seem to be trying to use time.Sleep to synchronize your go routines - in other words, so that main doesn't end before count.

This is the wrong way to synchronize goroutines. Keep in mind that most general purpose operating systems such as Linux and Windows do not guarantee timing; you need a real-time OS for that. So while you might usually get lucky and have the goroutines execute in the expected order, there is no guarantee that sleeps will make things happen in the expected order even on linux. The timing between these goroutines is simply not deterministic.

One of the correct ways to synchronize goroutines is with a sync.WaitGroup.

The following code works with or without the sleeps.

package main

import (
    "fmt"
    "sync"
)

func count(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
}

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go count(&wg)
    fmt.Println("Hello World")
    wg.Wait()
}

syc.WaitGroup is a convenient way to make sure the all goroutines are complete, in this case before the main function exits thus ending the program.

You could also do it with a channel:

package main

import (
    "fmt"
)

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

func main() {
    var done = make(chan bool)
    go count(done)
    fmt.Println("Hello World")
    <-done
}

Sign up to request clarification or add additional context in comments.

Comments

0

It is a well-known issue: starting from Go 1.16 time.Sleep in MS Windows uses low resolution timer, as bad as 1/64 second. Sleeping for 1 ms in Windows is anything between 1 and 16 ms.

Try printing microsecond timestamps:

package main

import (
    "fmt"
    "time"
)

func count() {
    fmt.Println(time.Now().Nanosecond() / 1000)
    for i := 0; i < 5; i++ {
        fmt.Println(i, time.Now().Nanosecond()/1000)
        time.Sleep(time.Millisecond * 1)
    }
}

func main() {
    // if runtime.GOOS == "windows" {
    //  initTimer()
    // }
    ts := time.Now().UnixNano()
    fmt.Println("Main started: ", ts, (ts%1_000_000_000)/1000)
    go count()
    time.Sleep(time.Millisecond * 2)
    fmt.Println("Hello World", time.Now().Nanosecond()/1000)
    time.Sleep(time.Millisecond * 5)
    fmt.Println("Main done", time.Now().Nanosecond()/1000)
}

On my Windows 10:

Main started:  1663345297398120000 398120
398703
0 398703
Hello World 405757
1 405757
2 421481
Main done 421481

The numbers are microseconds. See, how big are the intervals.

To improve timer resolution you can call timeBeginPeriod function.

//go:build windows
// +build windows

package main

import "syscall"

func initTimer() {
    winmmDLL := syscall.NewLazyDLL("winmm.dll")
    procTimeBeginPeriod := winmmDLL.NewProc("timeBeginPeriod")
    procTimeBeginPeriod.Call(uintptr(1))
}

Calling initTimer helps a lot:

Main started:  1663345544132793500 132793
132793
0 133301
Hello World 134854
1 134854
2 136403
3 137964
4 139627
Main done 140696

Still the resolution is not 1 ms, but better than 2 ms.

The full code is here: https://go.dev/play/p/LGPv74cgN_h

Discussion thread in Golang issues: https://github.com/golang/go/issues/44343

2 Comments

The program is not guaranteed to print anything other than "Hello World", so while the timer resolution may be interesting, neither output is more correct than the other.
@JimB from purist computer science point of view - yes, Go runtime gives no guarantees when to launch a goroutine. From practical point of view, the answer to the question of TS is the granularity of the default timer in MS Windows.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.