【问题标题】:Golang goroutine with timeGolang goroutine 与时间
【发布时间】:2021-07-06 02:24:03
【问题描述】:

我正在学习 Golang 并编写一些示例程序以熟悉不同的概念。 我写了这个样例来看看 go 例程是如何工作的并遇到了这个问题。 我无法弄清楚这个程序有什么问题。当 generateOddRandomNumbers() 和 geneateEvenRandomNumbers() 中的最后一行被注释掉时,它可以完美运行。

// time.Sleep(time.Duration(val))

当我在这两个函数中取消注释该行时,输出总是错误的。

代码:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

const (
    limit = 10000
)

func main() {
    rand.Seed(time.Now().UnixNano())
    go generateOddRandomNumbers(rand.Intn(9) + 1)
    go generateEvenRandomNumbers(rand.Intn(9) + 1)
    time.Sleep(1000000)
}

func generateOddRandomNumbers(count int) {
    for i := 0; i < count; i++ {
        val := rand.Intn(limit)
        if val % 2 == 0 {
            i--
            continue
        }
        fmt.Printf("Odd[%v/%v] -> %v\n", i + 1, count, val)
        // time.Sleep(time.Duration(val))
    }
}

func generateEvenRandomNumbers(count int) {
    for i := 0; i < count; i++ {
        val := rand.Intn(limit)
        if val % 2 != 0 {
            i--
            continue
        }
        fmt.Printf("Even[%v/%v] -> %v\n", i + 1, count, val)
        // time.Sleep(time.Duration(val))
    }
}

例如: 良好的输出:Even() 从 1/6 打印到 6/6,Odd() 从 1/3 打印到 3/3

Even[1/6] -> 5202
Even[2/6] -> 2768
Even[3/6] -> 3020
Even[4/6] -> 6386
Even[5/6] -> 8004
Odd[1/3] -> 5075
Odd[2/3] -> 8057
Odd[3/3] -> 9655
Even[6/6] -> 8886

错误输出:(当 time.Sleep() 未注释时) 在下面的例子中,Even 只在 1/9 到 3/9 之间打印(而不是 1/9 到 9/9)

Odd[1/1] -> 4349
Even[1/9] -> 6024
Even[2/9] -> 5444
Even[3/9] -> 6160

有什么想法吗?

【问题讨论】:

  • 我仍然对输出应该是什么样子感到困惑。即使我取消注释这些行,我也会得到不同的输出...play.golang.org/p/ornQbpLnU07

标签: go goroutine


【解决方案1】:

Go 程序不会等待您的 Go 例程完成。

  • 如果你在 goroutine 中评论 time.Sleep,你的 goroutine 运行很快,所以你得到一个完整的输出。

  • 如果你在 goroutine 中取消注释 time.Sleep,你的 goroutine 运行缓慢。一旦它产生调度程序,主 go 程序就完成了,所以你的 goroutine 没有机会完成整个运行。

您可以使用channelsync.WaitGroup 来同步您的程序,例如How to wait for all goroutines to finish in Golang

顺便说一句,对于time.Sleep(1000000),它的单位是ns,所以在你的例子中主go程序快速退出。

【讨论】:

  • 我没有意识到睡眠是以纳秒为单位的,这就是问题所在。我确实在主线程中进行了很大的等待,但这还不够大。我仍在加快频道和 WaitGroups 的速度,但只是想知道为什么那个简单的程序会失败。当我将睡眠时间增加到一个非常大的数字时,一切都按预期进行。谢谢!
  • 我刚刚发现,为了让大量的时间更具可读性。睡眠,建议使用time.Sleep(time.Second * 2)time.Sleep(time.Millisecond * 100)
【解决方案2】:

你的程序退出的原因是因为主线程没有等待生成的 goroutine 完成。您必须保持主线程打开。

一种方法是使用WaitGroups

使用 this 的修改版本示例,您可以执行以下操作:

Go Playground

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

const (
    limit = 10000
)

func main() {
    rand.Seed(time.Now().UnixNano())
    
    var wg sync.WaitGroup
    // Add our two items to the wait group
    wg.Add(2)
    
    // Call our two items
    go generateOddRandomNumbers(rand.Intn(9) + 1, &wg)
    go generateEvenRandomNumbers(rand.Intn(9) + 1, &wg)

    // Wait for them
    wg.Wait()
}

func generateOddRandomNumbers(count int, wg *sync.WaitGroup) {
    defer wg.Done();
    for i := 0; i < count; i++ {
        val := rand.Intn(limit)
        if val%2 == 0 {
            i--
            continue
        }
        fmt.Printf("Odd[%v/%v] -> %v\n", i+1, count, val)
        // time.Sleep(time.Duration(val))
    }
}

func generateEvenRandomNumbers(count int, wg *sync.WaitGroup) {
    defer wg.Done();
    for i := 0; i < count; i++ {
        val := rand.Intn(limit)
        if val%2 != 0 {
            i--
            continue
        }
        fmt.Printf("Even[%v/%v] -> %v\n", i+1, count, val)
        // time.Sleep(time.Duration(val))
    }
}
/** Which outputs:
Even[1/8] -> 2162
Even[2/8] -> 324
Even[3/8] -> 8388
Even[4/8] -> 7084
Even[5/8] -> 9282
Even[6/8] -> 302
Even[7/8] -> 6232
Even[8/8] -> 8384
Odd[1/3] -> 5063
Odd[2/3] -> 1239
Odd[3/3] -> 7947
*/

【讨论】:

  • 我的下一个更改是应用等待组,然后修改程序以使用通道以及动手实践 Go 中的不同主题,您已经回答了这个问题。
猜你喜欢
  • 1970-01-01
  • 2019-11-19
  • 2014-11-14
  • 2015-12-07
  • 2022-01-16
  • 1970-01-01
  • 2014-07-19
  • 2017-01-05
  • 2016-09-19
相关资源
最近更新 更多