Golang goroutine with time(使用时间的Golang goroutine)

huangapple go评论92阅读模式
英文:

Golang goroutine with time

问题

我正在学习Golang,并编写一些示例程序来熟悉不同的概念。我写了这个示例程序来了解Go协程的工作原理,并遇到了这个问题。我无法弄清楚这个程序有什么问题。当在generateOddRandomNumbers()和generateEvenRandomNumbers()函数的最后一行取消注释时,它可以正常工作。但是,一旦我在这两个函数中取消注释那一行,输出就总是错误的。

代码如下:

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

有什么想法吗?

英文:

I am in the process of learning Golang and writing some sample programs to familiarize myself with different concepts.
I wrote this sample piece to see how the go routines work and ran into this issue.
I couldn't figure out what is wrong with this program. It works perfectly when the last line in generateOddRandomNumbers() and geneateEvenRandomNumbers() is commented out.

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

The moment I uncomment that line in both the functions, the output is always wrong.

Code:

package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;time&quot;
)

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 &lt; count; i++ {
		val := rand.Intn(limit)
		if val % 2 == 0 {
			i--
			continue
		}
		fmt.Printf(&quot;Odd[%v/%v] -&gt; %v\n&quot;, i + 1, count, val)
		// time.Sleep(time.Duration(val))
	}
}

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

For example:
Good Output: Even() got printed from 1/6 to 6/6 and Odd() got printed 1/3 to 3/3

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

Bad Output: (when time.Sleep() is uncommented)
In the below case, Even got printed only from 1/9 to 3/9 (as opposed to 1/9 to 9/9)

Odd[1/1] -&gt; 4349
Even[1/9] -&gt; 6024
Even[2/9] -&gt; 5444
Even[3/9] -&gt; 6160

Any idea?

答案1

得分: 2

一个Go程序不会等待你的goroutine完成。

  • 如果你在goroutine中注释掉time.Sleep,你的goroutine会快速运行,所以你会得到完整的输出。

  • 如果你取消注释goroutine中的time.Sleep,你的goroutine会运行得很慢。一旦它让出调度器,主要的Go程序就会结束,所以你的goroutine没有机会完成整个运行。

你可以使用channelsync.WaitGroup来同步你的过程,就像如何等待所有goroutine完成中所示。

顺便说一下,对于time.Sleep(1000000),它的单位是纳秒(ns),所以在你的例子中,主要的Go程序会很快退出。

英文:

A Go program doesn’t wait for your go routines to finish.

  • If you comment time.Sleep in goroutine, you goroutine run quickly, so you get a full output.

  • If you uncomment time.Sleep in goroutine, you goroutine run slowly. Once it yield scheduler, the main go program finish, so your goroutine don't have chance to finish the whole run.

You may use channel or sync.WaitGroup to sync your procedure, like How to wait for all goroutines to finish in Golang.

BTW, for time.Sleep(1000000), its unit is ns, so main go program exit quickly in your example.

答案2

得分: 2

你的程序退出的原因是主线程没有等待派生的goroutine完成。你需要保持主线程的开放状态。

一种方法是使用WaitGroup

使用修改版本的这个示例,你可以这样做:

Go Playground

package main

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

const (
    limit = 10000
)

func main() {
	rand.Seed(time.Now().UnixNano())
	
	var wg sync.WaitGroup
	// 将两个任务添加到等待组中
	wg.Add(2)
	
	// 调用两个任务
	go generateOddRandomNumbers(rand.Intn(9) + 1, &wg)
	go generateEvenRandomNumbers(rand.Intn(9) + 1, &wg)

	// 等待它们完成
	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))
	}
}
/** 输出结果:
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
*/
英文:

The reason why your program exits is because the main thread doesn't wait on spawned goroutines to finish. You have to keep the main thread open.

One way to do this is with WaitGroups.

Using a modified version of this example, you could do something like:

Go Playground

package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

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, &amp;wg)
	go generateEvenRandomNumbers(rand.Intn(9) + 1, &amp;wg)

	// Wait for them
	wg.Wait()
}

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

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

huangapple
  • 本文由 发表于 2021年7月6日 10:24:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/68263792.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定