单通道上Goroutine解锁的顺序

时间:2014-09-16 04:27:17

标签: go channel

Goroutines在频道上阻止的顺序是否会决定他们取消阻止的顺序?我并不关心发送的消息的顺序(保证它们是有序的),而是关于将解锁的Goroutines的顺序。

想象一下,在多个Goroutines(1,2和3)之间共享一个空的频道ch,每个Goroutine都试图在ch上收到一条消息。由于ch为空,因此每个Goroutine都会阻止。当我向ch发送消息时,Goroutine 1会先取消阻止吗?或者2或3可能会收到第一条消息? (反之亦然,Goroutines试图发送)

我有一个playground,似乎表明Goroutines阻止的顺序是它们被解除阻塞的顺序,但我不确定这是否是一个未定义的行为,因为实现。

3 个答案:

答案 0 :(得分:4)

这是一个很好的问题 - 在进行并行设计时涉及一些重要问题。如前所述,根据当前的实施,您的具体问题的答案是基于FIFO。它可能不会有所不同,除非实施者决定,例如,LIFO由于某种原因更好。

无保证。因此,您应该避免在特定实现上创建依赖的代码。

更广泛的问题涉及非决定论公平饥饿

也许令人惊讶的是,基于CSP的系统中的非确定性并非来自并行发生的事情。它是可能因为并发,但不是因为并发的。相反,当做出选择时会出现非确定性。在CSP的正式代数中,这是以数学方式建模的。幸运的是,您不需要知道能够使用Go的数学。但正式地说,两个goroutines代码并行执行,如果所有选择都被消除,结果仍然可以是确定性的。

Go允许通过select明确地引入非确定性的选择,并通过goroutines之间共享的通道的末尾隐式地引入非确定性。如果您有点对点(一个阅读器,一个写入器)通道,则不会出现第二种通道。因此,如果它在特定情况下很重要,那么您可以做出设计选择。

公平饥饿通常是同一枚硬币的对立面。饥饿是那些动态问题(以及死锁,活锁和竞争条件)之一,可能导致表现不佳,更有可能出现错误行为。这些动态问题是不可测试的(more on this),需要进行一些级别分析才能解决。显然,如果系统的一部分没有响应,因为它缺乏对某些资源的访问权限,那么管理这些资源需要更加公平。

由于当前的FIFO行为,对通道端的共享访问可能提供一定程度的公平性,这似乎足够了。但是如果你想保证它(不管实现的不确定性),可以在数组中使用select和一束点对点通道。公平的索引很容易实现,总是按照将最后选择放在堆底部的顺序来选择它们。此解决方案可以保证公平,但可能会有很小的性能损失。

(另请参阅"Wot No Chickens",了解英国坎特伯雷研究人员关于Java虚拟机公平性缺陷的一个有趣的发现 - 这个问题从未得到纠正!) < / p>

答案 1 :(得分:2)

我认为它未指定,因为memory model document仅表示“在该频道的相应接收完成之前,频道上的发送已经发生。” send statementsthe receive operator上的规范部分没有说明首先取消阻止的内容。现在gc工具链uses是一个有序的FIFO队列来控制哪个goroutine解除阻塞,但是我没有在规范中看到它必须始终如此的承诺。

(只是为了一般背景说明,Playground代码在GOMAXPROCS = 1的情况下运行,即在一个核心上运行,因此某些类型的并发相关的不可预测性不会出现。)

答案 2 :(得分:1)

未指定顺序,但当前实现使用FIFO队列来等待goroutines。

权威文件是Go Memory Model。内存模型没有为发送到同一通道的两个goroutine定义发生在之前的关系,因此未指定顺序。同上接收。