Go语言之旅练习 #1:并发和go关键字

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

Tour of Go Exercise #1: Concurrency and the go keyword

问题

我正在进行《Go之旅》的学习,并且一直在编辑大部分课程,以确保我完全理解它们。我有一个关于 https://tour.golang.org/concurrency/1 的问题。

main保持原样会导致hello和world的随机顺序输出,因为线程在每次运行程序时以不同的顺序执行。我有两个问题:

  1. 如果我从带有world的那一行中删除go,并将其添加到带有hello的那一行,world会被打印5次,而hello则根本不会被打印。为什么会这样?
  2. 如果我在两行前面都添加go,则根本不会打印任何内容。为什么会这样?

我有一些使用C++进行并发编程的经验(虽然是一段时间之前的经验),并且最近还有一些使用Python的经验,但是我对并发编程的整体经验可以说是初级水平。

谢谢!

英文:

I'm going through 'A Tour of Go' and have been editing most of the lessons to make sure I fully understand them. I have a question regarding: https://tour.golang.org/concurrency/1

package main

import (
	"fmt"
	"time"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

func main() {
	go say("world")
	say("hello")
}

Leaving main the way it is produces a random ordering of hellos and worlds because the threads are executing in different orders each time the program runs. I have two questions:

  1. If I remove go from the line with world and add it to the line with hello, world is printed 5 times and hello is not printed at all. Why is that?
  2. If I add go in front of both lines, nothing is printed at all. Why is that?

I have some experience with concurrency using C++ (although it was a while ago) and some more recent experience with Python, but would describe my overall experience with concurrency fairly novice-level.

Thanks!

答案1

得分: 6

程序在你有机会看到结果之前终止。

你可以通过添加一条语句来修复这个问题,确保在其他程序完成之前,main 函数不会退出。


来自 Go - 并发

>使用 goroutine,我们立即返回到下一行,而不等待函数完成

他们给出了一个代码示例:

package main

import "fmt"

func f(n int) {
  for i := 0; i < 10; i++ {
    fmt.Println(n, ":", i)
  }
}

func main() {
  go f(0)
  var input string
  fmt.Scanln(&input)
}

关于上面的代码:

>这就是为什么包含了对 Scanln 函数的调用;如果没有它,程序将在打印所有数字之前退出

英文:

The program terminates before you get a chance to see the results.

You can fix this by adding a statement that ensures main doesn't exit before the other routines are finished.


From Go - Concurrency:

>With a goroutine we return immediately to the next line and don't wait for the function to complete.

They give a code example:

package main

import &quot;fmt&quot;

func f(n int) {
  for i := 0; i &lt; 10; i++ {
    fmt.Println(n, &quot;:&quot;, i)
  }
}

func main() {
  go f(0)
  var input string
  fmt.Scanln(&amp;input)
}

In regards to the code above:

>This is why the call to the Scanln function has been included; without it the program would exit before being given the opportunity to print all the numbers.

答案2

得分: 0

如果我从say("world")这一行中删除go关键字,并将其添加到say("hello")这一行中,那么world会被打印5次,而hello则根本不会被打印。为什么会这样呢?

如果我在这两行前面都加上go关键字,那么根本就不会打印任何内容。为什么会这样呢?

在这两种情况下,都出现了相同的问题,即执行了不同步的操作。这导致在所有启动的工作被处理之前,程序就从main函数返回了。规范指出,“当main函数调用返回时,程序退出。它不会等待其他(非主)goroutine完成。”

当你使用go关键字时,运行时会将函数调用的执行作为独立的并发线程。你需要使用同步语言原语来重新同步在调用main之后的操作退出顺序与剩余的异步任务。

语言提供了channel或者通过sync包提供的WaitGroup来帮助你实现这种行为。

例如:

package main

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

var wg sync.WaitGroup

func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
		wg.Done()
	}
}

func main() {
	wg.Add(5)
	say("world")
	wg.Add(5)
	say("hello")

	wg.Wait()
}
英文:

> If I remove the go keyword from the line say(&quot;world&quot;) and add it to the line say(&quot;hello&quot;), world is printed 5 times and hello is not printed at all. Why is that?

> If I add the go in front of both lines, nothing is printed at all. Why is that?

In both cases the same problem occurs, you are executing unsynchronized operations. It results in your program returning from main before all started work was processed. The specification states that When that [main] function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

When you use the go keyword, the runtime starts the execution of a function call as an independent concurrent thread of control. You need to use the synchronization language primitives to re synchronize the exit order of operations following the call to main with the remaining asynchronous jobs.

The language offers channels or otherwise WaitGroups via the sync package to help you implement that behavior.

For example

package main

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

var wg sync.WaitGroup

func say(s string) {
	for i := 0; i &lt; 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
		wg.Done()
	}
}

func main() {
	wg.Add(5)
	say(&quot;world&quot;)
	wg.Add(5)
	say(&quot;hello&quot;)

	wg.Wait()
}

huangapple
  • 本文由 发表于 2017年9月11日 08:26:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/46146761.html
匿名

发表评论

匿名网友

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

确定