How to explain channel block mechanism to golang beginners?

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

How to explain channel block mechanism to golang beginners?

问题

我是一名 Golang 初学者,在学习通道(channel)和 Go 协程(go routine)时遇到了一些困难。其中一个困惑点是 Golang 通道的阻塞机制是如何工作的。在 Golang 之旅 中,它说:“默认情况下,发送和接收操作会阻塞,直到另一侧准备好。”我根据自己的理解进行了一些实验,并得到了结果:“fatal error: all goroutines are asleep - deadlock!” 这个提示并没有给我太多关于错误发生原因的知识。我进行了一些谷歌搜索,但几乎每篇文章都是关于通道的深层机制,或者假设读者已经理解了单词“阻塞”并从中获得了所有信息。

但作为一个初学者,我有很多疑问,比如:

  • 同一个 Go 协程在写入通道后能否执行通道读取操作?
  • 如果一个通道被阻塞,其他 Go 协程是否仍然可以向其发送消息?
  • 如果一个 Go 协程尝试向一个被阻塞的通道发送消息,会发生什么?会抛出错误吗,还是什么都不会发生?

下面是一个让我困惑的测试示例(根据我的理解,main 函数是一个 Go 协程,并且我认为它在执行过程中是相同的):

package main

import "time"

func chanWrite(s string, ch chan string) {
	ch <- s
	println("write", s)
}

func chanRead(ch chan string) {
	println("read", <-ch)
}

// main func 1
func main() {
	ch := make(chan string)
	go chanWrite("A", ch)
	time.Sleep(time.Second)
}

// main func 1 没有抛出错误


// main func 2(修改 main func 1 并执行)
func main() {
	ch := make(chan string)
	chanWrite("A", ch)
	time.Sleep(time.Second)
}
// main func 2 抛出错误:"fatal error: all goroutines are asleep - deadlock!"

在这个测试中,我发现在某些情况下,当我向通道写入内容而没有读取时,我会得到“fatal error”,而有时什么都不会发生(例如在 main func 1 中)。

因此,有人能简单而有启发性地解释通道阻塞是如何工作的,以便 Golang 初学者能够轻松理解吗?

英文:

I'm a golang beginner and I've encountered some trouble when learning channel and go routine. One of my stuck point is how golang channel block mechanism works. In golang tour, it says By default, sends and receives block until the other side is ready. I did some experiments with my understanding, and met the result, fatal error: all goroutines are asleep - deadlock!. This hints didn't really give me much knowledge about how the error happens. I've googled it, but almost every article I got is about the deep mechanism of channel, or it assumes the reader understanding the single word block already gave out all the information.

But as a beginner I have many doubts, like:

  • Can same go-routine execute channel read operation after it writes to the channel?
  • If a channel blocked, can other go-routine still send message to it?
  • If a go-routine tries to send message to a blocked channel, what will happen? Will it throw an error, or nothing happened, or?

Below is one of my test which confuses me. (According to my knowledge, main func is a go-routine and I think it's the same one across its execution)

package main

import &quot;time&quot;

func chanWrite(s string, ch chan string) {
	ch &lt;- s
	println(&quot;write&quot;, s)
}

func chanRead(ch chan string) {
	println(&quot;read&quot;, &lt;- ch)
}

// main func 1
func main() {
	ch := make(chan string)
	go chanWrite(&quot;A&quot;, ch)
	time.Sleep(time.Second)
}

// main func 1 didn&#39;t throw error


// main func 2(modify main func 1 and then execute it)
func main() {
	ch := make(chan string)
	chanWrite(&quot;A&quot;, ch)
	time.Sleep(time.Second)
}
// main func 2 throw error: &quot;fatal error: all goroutines are asleep - deadlock!&quot;

What I saw in this test is in some condition, when I wrote things to the channel without reading it, I got fatal error, and sometimes nothing happened. (such like in main func 1).

So, can somebody explain how channel block works in a simple and inspiring way so that golang beginners can understand it easily?

答案1

得分: 2

一个goroutine可以在等待通道准备好发送或接收时阻塞,但通道本身永远不会处于“阻塞”状态。

第一个程序不会发生恐慌,因为主函数执行到最后并终止程序。

如果同一个goroutine在写入通道后执行通道读取操作,会发生什么?

如果通道是无缓冲的(默认情况下),不会发生这种情况。对于无缓冲通道,发送goroutine会阻塞,直到接收goroutine准备好接收。

单个goroutine不能同时执行发送和接收操作。如果只有一个goroutine向通道发送数据,该goroutine将永远阻塞。

如果一个通道被阻塞,其他goroutine还能向其发送消息吗?

一个goroutine可以在通道关闭之前的任何时候向通道发送值。一个goroutine可以阻塞等待通道准备好发送。

如果goroutine尝试向一个被阻塞的通道发送消息,会发生什么?会抛出错误吗,还是什么都不会发生?

一个goroutine可以阻塞等待通道准备就绪。通道本身不处于阻塞状态。

英文:

A goroutine can block waiting for a channel to be ready for send or receive, but the channel itself is never in a "blocked" state.

The first program does not panic because the main function executes to the end and terminates the program.

> Can same go-routine execute channel read operation after it writes to the channel?

No, if the channel is unbuffered (the default). For an unbuffered channel, a sending goroutine blocks until a receiving goroutine is ready.

A single goroutine cannot execute send and receive at the same time. If there's only one goroutine sending to the channel, the goroutine blocks forever.

> If a channel blocked, can other go-routine still send message to it?

A goroutine can send a value to a channel at any time before the channel is closed. A goroutine can block waiting for the channel to be ready for send.

> If go-routine trys to send message to a blocked channel, what will happened? Will it through an error, or nothing happened, or?

A goroutine can block waiting for the channel to be ready. The channel itself is not in a blocked state.

答案2

得分: 1

一个通道不会“阻塞”。对通道的读取或写入操作可能会阻塞。

对于无缓冲通道(就像你上面的例子):写入操作将阻塞写入的 goroutine,直到另一个 goroutine 准备从该通道读取。当读取的 goroutine 准备好读取(即执行 <-ch)时,写入和读取将同时进行,两个 goroutine 都会继续运行。

对于有缓冲的通道:只要通道缓冲区不满,写入操作将不会阻塞。同样,如果通道中有可读内容,读取操作也不会阻塞。

至于你的问题:

1)如果所讨论的通道是无缓冲的,写入操作将会阻塞,因为只有当另一个 goroutine 准备从通道中读取时,写入操作才能继续。因此,该 goroutine 将无法继续从通道中读取。如果这是一个有缓冲的通道,并且通道未满,同一个 goroutine 可以写入并读取通道。

2)如果一个 goroutine 正在等待写入一个通道,而另一个 goroutine 来写入它,它也会被阻塞,直到另一个 goroutine 从通道中读取。

3)如果一个 goroutine 尝试写入一个已满的通道,它将被阻塞,直到另一个 goroutine 可以从通道中读取。

英文:

A channel does not "block". A read or write operation on a channel may block.

For an unbuffered channel (which is what you have above): a write operation will block the writing goroutine until another goroutine is ready to read from that channel. When the reading goroutine is ready to read (i.e. executes <-ch), then the write and read take place, and both goroutines continue running.

For a buffered channel: a write will not block as long as the channel buffer is not full. Similarly, a read will not block if there is something to read from the channel.

As for your questions:

  1. If the channel in question is unbuffered, the write operation will block, because a write operation can continue only if another goroutine is reading from the channel. So the goroutine will not progress to read from the channel. If this is a buffered channel and if the channel is not full, the same goroutine may write to the channel and read from it.
  2. If a goroutine is waiting to write to a channel, and another goroutine comes to write to it, it will block as well until another goroutine reads fro the channel.
  3. If a goroutine tries to write to a channel that is full, it will block until another goroutine can read from the channel.

huangapple
  • 本文由 发表于 2021年6月7日 11:23:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/67865587.html
匿名

发表评论

匿名网友

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

确定