首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Windows中的Goroutine输出不像在Linux中那样完成

Windows中的Goroutine输出不像在Linux中那样完成
EN

Stack Overflow用户
提问于 2022-09-16 14:19:44
回答 2查看 60关注 0票数 1

我不明白为什么Windows中的Goroutine没有像Linux中的Goroutine那样正确地完成?

我已经在Powershell、VSCode、Goland甚至CMD中运行了代码,但是代码从来没有像Linux那样正确地完成。

以下是代码:

代码语言:javascript
运行
复制
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)
}

窗口输出:

代码语言:javascript
运行
复制
0
1
Hello World

Linux输出(这是预期的输出):

代码语言:javascript
运行
复制
0
1
2
Hello World
3
4

请帮助我理解或如何解决这个问题。

p/s:我刚开始学围棋。

EN

回答 2

Stack Overflow用户

发布于 2022-09-16 16:56:13

您似乎试图使用time.Sleep来同步您的go例程-换句话说,这样main就不会在count之前结束。

这是一种错误的同步方式。请记住,大多数通用操作系统(如Linux和Windows )并不保证定时;为此您需要一个实时操作系统。因此,虽然您通常会很幸运,让goroutines按照预期的顺序执行,但是即使在linux上,也不能保证睡眠会使事情按照预期的顺序发生。这些峡谷之间的时间安排是不确定的。

同步goroutines的正确方法之一是使用sync.WaitGroup

下面的代码适用于睡眠或不休眠。

代码语言:javascript
运行
复制
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都完成的一种方便的方法,在本例中是在主函数退出之前,从而结束程序。

你也可以用一个频道做这件事:

代码语言:javascript
运行
复制
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
EN

Stack Overflow用户

发布于 2022-09-16 16:32:59

这是一个众所周知的问题:从MS中的Go 1.16 time.Sleep开始使用低分辨率计时器,差到1/64秒。在Windows中睡眠1ms是介于1到16 ms之间的任何东西。

试着打印微秒时间戳:

代码语言:javascript
运行
复制
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上

代码语言:javascript
运行
复制
Main started:  1663345297398120000 398120
398703
0 398703
Hello World 405757
1 405757
2 421481
Main done 421481

数字是微秒。看,间隔有多大。

要提高计时器分辨率,可以调用timeBeginPeriod函数。

代码语言:javascript
运行
复制
//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非常有帮助:

代码语言:javascript
运行
复制
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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73746214

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档