我跟随Go Tour并且在涉及到goroutines时我有点卡住了。我知道他们非常轻量级,每次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")
}
据我所知,对于具有参数“world”的say函数启动了goroutine,但据我所知,应该打印“world”五次并“hello”一次。但是我不明白为什么输出是这样的:
hello
world
hello
world
hello
world
hello
world
hello
根据我对其他语言的线程的有限理解,输出应该是这样的:
hello
world
world
world
world
world
或者像这样:
world
world
world
hello
world
world
为什么第二行也执行五次?低于go
语句的任何内容都会归类为go例程的一部分吗?
下一张幻灯片还显示了一些我无法重新审视的内容:
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)
}
为切片的后半部分启动goroutine,然后为切片的第一部分启动另一个goroutine,但为值x
和y
分配了两个不同的值。我看到sum
函数的方式会将它的总和发送到频道c
,然后下一个sum
会将它的总和发送到同一频道c
那么两个怎么办呢?变量被分配两个不同的值?渠道c
不应该只有一个sum
值吗?
我很欣赏这是一个很长的问题,但我无法找到这些问题的答案。
答案 0 :(得分:5)
为什么第二行也会执行5次?
第二行将在hello
帖子中每隔5次打印main()
但同时第一行go say("world")
也将在一个单独的goroutine中每隔五秒打印一次世界。
Sleep
确保每个例程产生,允许另一个例程恢复。
因此输出:
hello
world
hello
world
hello
world
hello
world
hello
我看到它的方式sum函数会将它的总和发送到通道c,然后下一个和将它的总和发送到同一个通道c所以如何将两个变量分配给两个不同的值?
因为每次发送都会在c
上阻止,直到读取频道c
为止
由于有两个写入c
,您需要阅读:
x, y := <-c, <-c // receive from c twice.
Assignement section of Golang Spec允许在以下情况下进行元组分配:
左边的操作数必须等于右边的表达式数,每个表达式必须是单值的,右边的
nth
表达式分配给左边的第n个操作数。
答案 1 :(得分:1)
对于第一个函数,您应该看到VonC样式中的值。 hello
打印5次的原因是因为函数say
打印了5次。想象一下没有goroutine的程序。我认为它并不能保证你会完全穿插hello
和world
,但我可能错了。
频道有效的原因是:
empty out
,即当您将c
分配给x
时,它会删除传入频道的第一笔金额,以及何时将c
分配给y
它传递了第二个值(我认为顺序不是保证,因为x
可以有前半部分,y
可以有后半部分,反之亦然。如果你把频道想象成一种队列我觉得它更有意义。求和goroutines将值推送到队列中,赋值按顺序弹出值。