英文:
Goroutine + channel execution order
问题
我是你的中文翻译助手,以下是你提供的代码的翻译:
我是golang的新手,想深入了解channel
的工作原理。
过去我用.net
做过一个基本的多线程程序,所以我对goroutine
有一些了解。
我认为goroutine
是一种线程(实际上是一种绿色线程),它可以与其他goroutine
并发运行,所以不能保证执行顺序。
但是我不明白的是当你结合goroutine
和channel
时会发生什么。
我知道你可以使用channel
来保证goroutine
的执行顺序,因为它可以阻塞goroutine
。
假设我有这样的代码:
func greet(c chan string) {
fmt.Println("Before print 1st item")
fmt.Println("Hello", <-c)
fmt.Println("After print 1st item")
fmt.Println("Before print 2nd item")
fmt.Println("Hello", <-c)
fmt.Println("After print 2nd item")
}
func main() {
fmt.Println("Main start")
c := make(chan string)
go greet(c)
fmt.Println("Before insert 1st item")
c <- "Alpha" // 主goroutine在这里被阻塞,并切换到另一个goroutine。被阻塞是因为通道已满,或者我理解错了吗?
fmt.Println("After insert 1st item")
fmt.Println("Before insert 2nd item")
c <- "Zebra" // 为什么这里没有被阻塞?
fmt.Println("After insert 2nd item")
time.Sleep(2 * time.Second)
fmt.Println("Main end")
}
输出结果为:
Main start
Before insert 1st item
Before print 1st item
Hello Alpha
After print 1st item
Before print 2nd item
After insert 1st item
Before insert 2nd item
After insert 2nd item // 为什么会在这里打印?
Hello Zebra
After print 2nd item
Main end
为什么会这样打印?
我的预期是:
Main start
Before insert 1st item
Before print 1st item
Hello Alpha
After print 1st item
Before print 2nd item
After insert 1st item
Before insert 2nd item
Hello Zebra
After print 2nd item
After insert 2nd item
Main end
我理解的关键点是:
main
goroutine 在这一行c <- "Alpha"
被阻塞,所以切换到了greet
goroutine。greet
goroutine 将继续执行,直到在第二个fmt.Println("Hello", <-c)
上被阻塞。- 现在
greet
goroutine 被阻塞,而main
goroutine 没有被阻塞,因为通道中的字符串Alpha
已经被读取,所以它将继续执行。
我的问题是为什么 main
goroutine 在这一行 c <- "Zebra"
上没有被阻塞?
我预期它会像步骤2那样再次被阻塞,并切换到 greet
goroutine。
根据这个文档:
默认情况下,发送和接收操作会阻塞,直到另一侧准备就绪。这允许goroutine在没有显式锁或条件变量的情况下进行同步。
英文:
I'm new to golang and want to understand deeply how channel
works.
In the past I've made a basic multithreaded program with .net
so I kinda now what goroutine
is
I assume that goroutine
is some kind of thread (actually a green thread) that will run concurrently with other goroutine
so there's no guarantee for the execution order
but what I don't understand when you combine goroutine
and channel
I know that you can use channel
to guarantee the execution order of goroutine
because it can block goroutine
Lets say I have a code like this
func greet(c chan string) {
fmt.Println("Before print 1st item")
fmt.Println("Hello", <-c)
fmt.Println("After print 1st item")
fmt.Println("Before print 2nd item")
fmt.Println("Hello", <-c)
fmt.Println("After print 2nd item")
}
func main() {
fmt.Println("Main start")
c := make(chan string)
go greet(c)
fmt.Println("Before insert 1st item")
c <- "Alpha" // main goroutine blocked here and will be switched to another goroutine. Blocked because the channel is full or maybe I'm wrong here?
fmt.Println("After insert 1st item")
fmt.Println("Before insert 2nd item")
c <- "Zebra" // why is it not blocked here ?
fmt.Println("After insert 2nd item")
time.Sleep(2 * time.Second)
fmt.Println("Main end")
}
//Output
Main start
Before insert 1st item
Before print 1st item
Hello Alpha
After print 1st item
Before print 2nd item
After insert 1st item
Before insert 2nd item
After insert 2nd item // why is it printed here ???
Hello Zebra
After print 2nd item
Main end
Why is it printed like that ???
// My expectation
Main start
Before insert 1st item
Before print 1st item
Hello Alpha
After print 1st item
Before print 2nd item
After insert 1st item
Before insert 2nd item
Hello Zebra
After print 2nd item
After insert 2nd item
Main end
KeyPoint from what I understand
- The
main
gouroutine
blocked on this linec <- "Alpha"
so it switched togreet
goroutine - The
greet
goroutine
will continue to execute until it blocked on the secondfmt.Println("Hello", <-c)
- Now
greet
goroutine
is blocked and themain
goroutine
is not blocked because the stringAlpha
inchannel
already read and it will continue to execute
My question is why main
goroutine
is not blocked on this line c <- "Zebra"
?
I expected it to be blocked again just like in step 2 and will switch to greet
goroutine
Based on this doc
> By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
答案1
得分: 1
假设通道就像一张桌子。当有人等待从通道接收时,就好像你想从桌子上拿走东西。如果有东西,你就拿走然后离开;如果没有东西,你必须等待别人把东西放在那里。当你想要发送到通道时,情况也是一样的。如果桌子上没有东西,你就把你手上的东西放上去然后离开;如果桌子上有东西,你必须等待别人把东西拿走以便放上你的东西。(记住,你可以有一个可以容纳多个东西的通道)。
一旦你被释放,你可以随心所欲地做任何事情,不需要等待或同步。所以你无法预先知道会发生什么,因为取决于你被分配的处理周期,你可能会在其他例程之前或之后到达。
如果你多次运行这个程序,你会发现不同的输出出现。
英文:
Let's say channels are like a table. When somebody waits to receive from it, it is like you want to take something from the table. If there is anything, you take it and go, if there is nothing, you must wait for someone to put it over there. Same happens when you want to send at the channel. If there is nothing over the table, you just put the thing you have and leave, if there is something on it, you must wait for someone to take it to put your thing. (remember you can have a channel with space for more that one thing).
At the moment you get liberated, you keep doing whatever you want, no need to wait or syncronize. So you can't know what will happen before, because depending on the processing cycles you have been assigned you will get there before or after the other routines.
If you launch that a few times, you will see that different output appears.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论