为什么我的被认为是并行的 Go 程序却不是并行的?

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

Why is my supposedly parallel go program not parallel

问题

你遇到的问题是输出没有按照你期望的顺序混合输出,而是先输出完字母再输出数字。这是因为在Go语言中,goroutine的执行顺序是不确定的,它们是并发执行的。在你的代码中,你创建了两个goroutine,一个用于输出字母,一个用于输出数字。由于goroutine的执行顺序是不确定的,所以有可能先执行完输出字母的goroutine,然后再执行输出数字的goroutine,这样就导致了输出的顺序不是你期望的混合输出。

如果你希望输出的顺序是混合的,你可以使用其他的同步机制来控制goroutine的执行顺序,比如使用channel或者互斥锁。这样可以确保在一个goroutine执行完之前,另一个goroutine不会开始执行。

以下是使用channel来控制goroutine执行顺序的修改后的代码:

package main

import (
	"fmt"
	"runtime"
)

func alphabets(ch chan bool) {
	for char := 'a'; char < 'a'+26; char++ {
		fmt.Printf("%c ", char)
	}
	ch <- true //发送信号表示字母输出完成
}

func numbers(ch chan bool) {
	for number := 1; number < 27; number++ {
		fmt.Printf("%d ", number)
	}
	ch <- true //发送信号表示数字输出完成
}

func main() {
	runtime.GOMAXPROCS(2)

	ch1 := make(chan bool)
	ch2 := make(chan bool)

	fmt.Println("Starting Go Routines")
	go alphabets(ch1)
	go numbers(ch2)

	fmt.Println("\nWaiting To Finish")

	<-ch1 //等待字母输出完成
	<-ch2 //等待数字输出完成

	fmt.Println("\nTerminating Program")
}

这样修改后,通过使用channel来进行同步,可以确保在一个goroutine执行完之前,另一个goroutine不会开始执行,从而实现了混合输出的效果。

英文:
package main

import (
	&quot;fmt&quot;
	&quot;runtime&quot;
	&quot;sync&quot;
)

var wg sync.WaitGroup

func alphabets() {
	for char := &#39;a&#39;; char &lt; &#39;a&#39;+26; char++ {
		fmt.Printf(&quot;%c &quot;, char)
	}
	wg.Done() //decrement number of goroutines to wait for
}

func numbers() {
	for number := 1; number &lt; 27; number++ {
		fmt.Printf(&quot;%d &quot;, number)
	}
	wg.Done()
}

func main() {
	runtime.GOMAXPROCS(2)
	wg.Add(2) //wait for two goroutines

	fmt.Println(&quot;Starting Go Routines&quot;)
	go alphabets()
	go numbers()

	fmt.Println(&quot;\nWaiting To Finish&quot;)

	wg.Wait() //wait for the two goroutines to finish executing

	fmt.Println(&quot;\nTerminating Program&quot;)
}

I expect the output to be mixed up(for lack of a better word), but instead; a sample output is:

$ go run parallel_prog.go

Starting Go Routines
Waiting To Finish
a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Terminating Program

What I'm I missing?

Thanks,

答案1

得分: 6

你没有错。它是有效的。调用没有显示为“交错”(混合),不是因为它们没有并行化,而是因为它们发生得非常快。

你可以轻松地添加一些对time.Sleep的调用,以更好地观察并行化。通过睡眠,我们可以确保打印alphabetsnumbers应该是交错的。

以下是带有Sleep调用的程序,以“强制”交错:

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func alphabets() {
	defer wg.Done()
	for char := 'a'; char < 'a'+26; char++ {
		fmt.Printf("%c ", char)
		time.Sleep(time.Second * 2)
	}
}

func numbers() {
	defer wg.Done()
	for number := 1; number < 27; number++ {
		fmt.Printf("%d ", number)
		time.Sleep(time.Second * 3)
	}    
}

func main() {
	fmt.Println("Starting Go Routines")
	wg.Add(2)
	go alphabets()
	go numbers()

	fmt.Println("\nWaiting To Finish")
	wg.Wait()
	fmt.Println("\nTerminating Program")
}

注意:

你可能已经知道这一点,但设置GOMAXPROCS对于是否并行执行此示例没有任何影响,只影响它消耗多少资源。

GOMAXPROCS设置控制操作系统线程尝试同时执行代码的数量。例如,如果GOMAXPROCS为4,则程序一次只会在4个操作系统线程上执行代码,即使有1000个goroutine。此限制不计算在系统调用(如I/O)中被阻塞的线程。

来源:Go 1.5 GOMAXPROCS Default

英文:

You're missing nothing. It's working. The calls aren't showing up "interlaced" (mixed up) not because they're not being parallelized, but because they're happening really fast.

You can easily add some calls to time.Sleep to see the parallelization better. By sleeping, we know 100% that printing alphabets and numbers should be interlaced.

Your program with Sleep calls to "force" interlacing

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

var wg sync.WaitGroup

func alphabets() {
	defer wg.Done()
	for char := &#39;a&#39;; char &lt; &#39;a&#39;+26; char++ {
		fmt.Printf(&quot;%c &quot;, char)
		time.Sleep(time.Second * 2)
	}
}

func numbers() {
	defer wg.Done()
	for number := 1; number &lt; 27; number++ {
		fmt.Printf(&quot;%d &quot;, number)
		time.Sleep(time.Second * 3)
	}    
}

func main() {
	fmt.Println(&quot;Starting Go Routines&quot;)
	wg.Add(2)
	go alphabets()
	go numbers()

	fmt.Println(&quot;\nWaiting To Finish&quot;)
	wg.Wait()
	fmt.Println(&quot;\nTerminating Program&quot;)
}

Note

You probably already know this, but setting GOMAXPROCS doesn't have any effect on whether or not this example is executed in parallel, just how many resources it consumes.

> The GOMAXPROCS setting controls how many operating systems threads attempt to execute code simultaneously. For example, if GOMAXPROCS is 4, then the program will only execute code on 4 operating system threads at once, even if there are 1000 goroutines. The limit does not count threads blocked in system calls such as I/O.

Source: Go 1.5 GOMAXPROCS Default

答案2

得分: 3

你是否在使用Go Playground?当我在本地运行你的代码时,我得到的结果是:

启动Go协程

等待完成
1 2 3 4 5 6 7 8 9 10 11 12 a 13 14 15 16 17 b 18 19 c 20 21 d 22 23 e 24 25 f 26 g h i j k l m n o p q r s t u v w x y z 
终止程序

Playground是确定性的。Goroutines不会频繁切换,并且不会在多个线程中运行。

英文:

Are you using the Go playground by any chance? When I run your code locally, I get:

Starting Go Routines

Waiting To Finish
1 2 3 4 5 6 7 8 9 10 11 12 a 13 14 15 16 17 b 18 19 c 20 21 d 22 23 e 24 25 f 26 g h i j k l m n o p q r s t u v w x y z 
Terminating Program

The playground is deterministic in nature. Goroutines don't yield as often and don't run in multiple threads.

huangapple
  • 本文由 发表于 2015年7月8日 22:43:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/31296054.html
匿名

发表评论

匿名网友

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

确定