发送到频道的消息会丢失吗?

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

can a message sent to channel be lost?

问题

问题是关于标题的。假设我有多个 goroutine(超过100个),它们最终都会向一个通道发送数据(命名为 mychan := make(chan int))。另一个 goroutine 在一个无限循环中执行 <- mychan。这样做是否安全,通道是否会丢失一些数据?我应该使用带缓冲的通道吗?或者我应该创建一个通道和一个“守护” goroutine,为每个工作 goroutine 提取消息?

英文:

The question is in the title. Let's say I have several goroutines (more than 100) all of which eventually send data to one chan (name it mychan := make(chan int)) One another goroutine does <- mychan in an endless for loop Is it okay or the chan can happen to lose some data? Should I use buffered chan instead? Or perhaps I am to create a chan and a "demon" goroutine that will extract message for each worker goroutine?

答案1

得分: 2

不,它们不会丢失。

虽然语言规范对通道的实现没有任何特定要求,但你可以将它们视为信号量,用于保护单个值(用于单个消息)或数组/列表(用于缓冲通道)。

然后,语义是这样强制执行的:一旦一个 goroutine 想要向通道发送消息,它会尝试使用信号量来获取一个空闲的数据槽,然后要么成功发送(即有一个空闲槽可用于其消息),要么阻塞(当没有可用槽时)。一旦出现这样的槽(即有人接收到了现有的消息),发送就会成功,并且发送的 goroutine 将解除阻塞状态。

这是一个简化的解释。换句话说,在 Go 中,通道不像通常会丢失消息的消息队列。

顺便说一下,我不太确定如果接收者在某个特定状态下发生 panic 时会发生什么。换句话说,我不确定在接收者在不幸的时刻发生 panic 时,Go 是否保证消息已发送或未发送。

哦,还有一个灰色地带,即主 goroutine 退出(运行 main.main() 函数的那个):规范明确指出,主 goroutine 在退出时不会等待其他 goroutine 完成。因此,除非你以某种方式安排所有衍生 goroutine 的同步控制关闭,否则它们可能会丢失消息。另一方面,在这种情况下,世界已经崩溃了…

英文:

No, they can't be lost.

While the language spec does not in any way impose any particular implementation on channels, you can think of them as semaphores protecting either a single value (for the single message) or an array/list of them (for buffered channels).

The semantics are then enforced in such a way that as soon as a goroutine wants to send a message to a channel, it tries to acquire a free data slot using that semaphore, and then either succeeds at sending—there's a free slot for its message—or blocks—when there isn't. As soon as such a slot appears—someone has received an existing message—the sending succeeds and the sending goroutine gets unblocked.

This is a simplified explanation. In other words, channels in Go is not like message queues which usually are happy with losing messages.

On a side note, I'm not really sure what happens if the receiver panics in some specific state when it's about to receive your message. In other words, I'm not sure whether Go guarantees that the message is either sent or not in the presence of a receiver panicking in an unfortunate moment.

Oh, and there's that grey area of the main goroutine exiting (that one running the main.main() function): the spec states clear than the main goroutine does not wait for any other goroutines to complete when it exits. So unless you somehow arrange for the synchronized controlled shutdown of all your spawned goroutines, I believe they may lose messages. On the other hand, in this case the world is collapsing anyway…

答案2

得分: 2

如果某个消息成功发送到通道中,那么在正常工作环境下是不会丢失的(我指的是如果你篡改了内存或者由于宇宙射线导致了位翻转,那当然不会有任何期望)。

ch <- x 返回时,消息就成功发送了。否则,如果发生了 panic,那么实际上消息并没有被发送,如果你没有进行恢复,你可以说消息丢失了(不过,这是由于应用逻辑导致的)。发生 panic 的情况可能是通道被关闭,或者你的内存不足。

同样地,如果发送者在非阻塞模式下将消息放入通道(使用 select),你应该在通道中有足够的缓冲区,因为消息可能会被“丢失”(尽管这是有意为之的)。例如 signal.Notify 就是这样工作的:

signal 包不会阻塞发送到 c 的操作:调用者必须确保 c 有足够的缓冲区来跟上预期的信号速率。

英文:

If something has been successfully sent into the channel then no, it can't be lost in correctly working environment (I mean if you're tampering with your memory or you have bit flips due to cosmic rays then don't expect anything of course).

Message is successfully sent when ch &lt;- x returns. Otherwise, if it panics, it's not really being sent and if you don't recover than you could claim it's lost (however, it would be lost due to application logic). Panic can happen if channel is closed or, say, you're out of memory.

Similarly if sender is putting into the channel in non-blocking mode (by using select), you should have a sufficient buffer in your channel, because messages can be "lost" (although somehow intentionally). For example signal.Notify is working this way:

> Package signal will not block sending to c: the caller must ensure that c has sufficient buffer space to keep up with the expected signal rate.

答案3

得分: 0

消息不能丢失。它可能没有被发送。goroutine的执行顺序没有定义。因此,你的无限for循环只能一直从一个worker接收,甚至如果它不在主线程中,还可以休眠。为了确保你的队列按照正常方式工作,最好在“main”函数中为每个worker显式地接收消息。

英文:

Message can not be lost. It can be not sent.Order of goroutines execution not defined. So your endless for loop can receive from only one worker all time, and even can sleep if it isn't in main thread. To be sure your queue works in regular fashion you better explicitly in 'main' receive messages for each worker.

huangapple
  • 本文由 发表于 2015年12月25日 22:47:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/34463793.html
匿名

发表评论

匿名网友

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

确定