我不明白为什么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)
}
窗口输出:
0
1
Hello World
Linux输出(这是预期的输出):
0
1
2
Hello World
3
4
请帮助我理解或如何解决这个问题。
p/s:我刚开始学围棋。
发布于 2022-09-16 16:56:13
您似乎试图使用time.Sleep
来同步您的go例程-换句话说,这样main
就不会在count
之前结束。
这是一种错误的同步方式。请记住,大多数通用操作系统(如Linux和Windows )并不保证定时;为此您需要一个实时操作系统。因此,虽然您通常会很幸运,让goroutines按照预期的顺序执行,但是即使在linux上,也不能保证睡眠会使事情按照预期的顺序发生。这些峡谷之间的时间安排是不确定的。
同步goroutines的正确方法之一是使用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()
}
syc.WaitGroup
是确保所有goroutines都完成的一种方便的方法,在本例中是在主函数退出之前,从而结束程序。
你也可以用一个频道做这件事:
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
}
发布于 2022-09-16 16:32:59
这是一个众所周知的问题:从MS中的Go 1.16 time.Sleep
开始使用低分辨率计时器,差到1/64秒。在Windows中睡眠1ms是介于1到16 ms之间的任何东西。
试着打印微秒时间戳:
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
不过,决议不是1ms,而是比2ms更好。
完整的代码在这里:https://go.dev/play/p/LGPv74cgN_h
戈朗问题中的讨论主题:https://github.com/golang/go/issues/44343
https://stackoverflow.com/questions/73746214
复制相似问题