英文:
How do goroutines work?
问题
我正在为你翻译以下内容:
我正在按照Go Tour的教程学习,但在使用goroutines时遇到了一些困难。我知道goroutines非常轻量级,每当一个goroutine被阻塞时,另一个goroutine就会开始执行,但我无法理解这个示例是如何工作的:
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(1000 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
我知道一个goroutine会为带有参数"world"的say函数启动,但据我了解,它应该打印"world"五次和"hello"一次。然而,我不明白为什么输出结果是这样的:
hello
world
hello
world
hello
world
hello
world
hello
根据我对其他语言中线程的有限理解,输出结果应该是这样的:
hello
world
world
world
world
world
或者是这样的:
world
world
world
hello
world
world
为什么第二行也执行了五次?在go
语句下面的任何内容都被视为goroutine的一部分吗?
另外,下一张幻灯片展示了另一个我无法理解的例子:
package main
import "fmt"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum // 将sum发送到c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // 从c接收数据
fmt.Println(x, y, x+y)
}
一个goroutine被启动来处理切片的后半部分,然后另一个goroutine被启动来处理切片的前半部分,然而变量x
和y
被赋予了两个不同的值。在我看来,sum
函数会将其求和结果发送到通道c
,然后下一个sum
函数会将其求和结果发送到同一个通道c
,那么这两个变量为什么会被赋予两个不同的值?通道c
不应该只有一个sum
值吗?
我很感谢你提出这个相当长的问题,但我无法找到这些问题的答案。
英文:
I was following the Go Tour and I am a bit stuck when it comes to goroutines. I understand that they are very lightweight and that every time a goroutine blocks, another one will start but I can't get my head around how this example actually works:
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(1000 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
I understand that a goroutine is started for the say function with the argument "world", but as far as I understand that should print "world" five times and "hello" once. However I don't understand why the output is as it is:
hello
world
hello
world
hello
world
hello
world
hello
From my limited understanding of threads from other languages the output should have been something like this:
hello
world
world
world
world
world
or like this:
world
world
world
hello
world
world
Why does the second line execute five times as well? Does anything below a go
statement classify as part of the go routine?
Also the next slide shows something I can't get my head round again:
package main
import "fmt"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum // send sum to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
A goroutine is started for the second half of the slice and then another one for the first part of the slice, however the values x
and y
have been assigned two different values. The way I see it the sum
function will send it's sum to channel c
and then the next sum
will send it's sum to the same channel c
so how can the two variables be assigned two different values? Shouldn't channel c
have one single sum
value in there?
I appreciate that this is quite a long question but I wasn't able to find the answer to these questions.
答案1
得分: 5
为什么第二行也执行了5次?
第二行在main()
线程中每秒执行5次打印hello
。但是同时,第一行go say("world")
也会在一个单独的goroutine中每秒执行5次打印world
。Sleep
函数确保每个goroutine都会暂停,让其他goroutine继续执行。
因此输出结果为:
hello
world
hello
world
hello
world
hello
world
hello
在我看来,sum
函数会将其求和结果发送到通道c
,然后下一个求和结果也会发送到同一个通道c
,那么这两个变量如何被赋予不同的值呢?
因为每次发送操作都会阻塞,直到通道c
被读取。由于有两次向c
发送数据,你需要进行两次读取:
x, y := <-c, <-c // 从c中接收两次数据
在Golang规范的赋值部分中,允许进行元组赋值,条件是:
左边的操作数数量必须等于右边的表达式数量,每个表达式都必须是单值的,第n个表达式会被赋值给左边的第n个操作数。
英文:
> Why does the second line execute 5 times as well?
The second line will print hello
every second 5 times in the main()
thread.
But concurrently the first line go say("world")
will also print world every seconds five times in a separate goroutine.
The Sleep
ensure that each routine yields, allowing the other to resume.
Hence the output:
hello
world
hello
world
hello
world
hello
world
hello
> The way I see it the sum function will send it's sum to channel c and then the next sum will send it's sum to the same channel c so how can the two variables be assigned two different values?
Because each send will block on c
until channel c
is read.
Since there are two write to c
, you need to read:
x, y := <-c, <-c // receive from c twice.
The Assignement section of Golang Spec allows for a tuple assignment if:
> the number of operands on the left must equal the number of expressions on the right, each of which must be single-valued, and the nth
expression on the right is assigned to the nth operand on the left.
答案2
得分: 1
对于第一个函数,你应该看到VonC提供的值的样式。hello
打印5次的原因是因为say
函数打印了5次。想象一下没有goroutine的程序。我认为它不能保证你会完美地交替得到hello
和world
,但我可能错了。
通道工作的原因是:
- Golang允许你进行多重赋值,就像VonC提到的那样。
- 通道会"清空",也就是说当你将
c
赋值给x
时,它会移除传递到通道中的第一个和值,当你将c
赋值给y
时,它会传递第二个值(同样,我认为顺序不是一个保证,因为x
可能有前半部分和值,y
有后半部分和值,或者反过来)。
如果你将通道想象成一种队列,我认为会更容易理解。求和的goroutine将值推入队列,而赋值操作按顺序弹出这些值。
英文:
For the first function you should see values in the style VonC presented. The reason hello
prints 5 times as well is because the function say
prints things 5 times. Just imagine the program without the goroutine. I think it doesn't guarantee that you will get hello
and world
perfectly interspersed but I may be wrong.
The reason the channel works is:
- Golang let's you do multiple assignment as VonC mentions
- Channels
empty out
, i.e. when you assignc
tox
it removes the first sum that was passed into the channel and when it assignsc
toy
it passes in the second value (again I think the order is not a guarantee asx
could have the first half andy
the second half sum or vice-versa.
If you imagine channels as a sort of a queue I think it makes more sense. The summing goroutines push values onto the queue and assignments pop the values sequentially.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论