英文:
Goroutine Output in Windows not finish like in Linux
问题
我不明白为什么在Windows中的Goroutine无法像Linux中的Goroutine一样正确结束。我已经在Powershell、VSCode、Goland甚至CMD中运行了这段代码,但是代码从未像Linux输出那样正确结束。
以下是代码:
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输出:
0
1
Hello World
Linux输出(预期输出):
0
1
2
Hello World
3
4
请帮助我理解或修复这个问题。
附注:我刚开始学习Go语言。
英文:
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
你似乎正在尝试使用time.Sleep
来同步你的Go协程,换句话说,确保main
在count
之前不会结束。
这是错误的同步Go协程的方式。请记住,大多数通用操作系统(如Linux和Windows)不保证时间性;你需要一个实时操作系统来实现这一点。因此,虽然你可能通常会幸运地按预期顺序执行Go协程,但不能保证睡眠会按预期顺序发生,即使在Linux上也是如此。这些Go协程之间的时间间隔是不确定的。
同步Go协程的一种正确方式是使用sync.WaitGroup
。
以下代码可以在有或没有睡眠的情况下工作。
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()
}
sync.WaitGroup
是一种方便的方式,可以确保所有的Go协程都完成,在这种情况下,确保在主函数退出之前结束程序。
你也可以使用通道来实现:
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
}
英文:
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
}
答案2
得分: 0
这是一个众所周知的问题:从Go 1.16开始,MS Windows上的time.Sleep
使用低分辨率定时器,最差只有1/64秒。在Windows上睡眠1毫秒的时间可能在1到16毫秒之间。
尝试打印微秒级时间戳:
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)
}
在我的Windows 10上运行结果为:
Main started: 1663345297398120000 398120
398703
0 398703
Hello World 405757
1 405757
2 421481
Main done 421481
这些数字是微秒。可以看到时间间隔有多大。
为了改善定时器的分辨率,你可以调用timeBeginPeriod
函数。
//go:build windows
// +build windows
package main
import "syscall"
func initTimer() {
winmmDLL := syscall.NewLazyDLL("winmm.dll")
procTimeBeginPeriod := winmmDLL.NewProc("timeBeginPeriod")
procTimeBeginPeriod.Call(uintptr(1))
}
调用initTimer
会有很大帮助:
Main started: 1663345544132793500 132793
132793
0 133301
Hello World 134854
1 134854
2 136403
3 137964
4 139627
Main done 140696
分辨率仍然不是1毫秒,但比2毫秒要好。
完整的代码在这里:https://go.dev/play/p/LGPv74cgN_h
Golang问题讨论线程:https://github.com/golang/go/issues/44343
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论