单通道Goroutine解锁顺序



Goroutines在通道上的阻塞顺序是否决定了它们将取消阻塞的顺序?我不关心发送的消息的顺序(它们保证是有序的),而是将解锁的Goroutines的顺序。

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

我有一个游乐场,似乎表明Goroutines阻塞的顺序就是它们被解除阻塞的顺序,但我不确定这是否是由于实现而导致的未定义行为。

这是一个很好的问题,它涉及到在进行并行设计时的一些重要问题。如前所述,根据目前的实施方式,您的具体问题的答案是基于先进先出。这不太可能有什么不同,除非实施者出于某种原因决定,比如说,后进先出法更好。

不过,并不能保证。因此,您应该避免创建依赖特定实现的代码。

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

也许令人惊讶的是,基于CSP的系统中的非决定论并非来自并行发生的事情。由于并发性,可能,但由于并发性而不可能。相反,当做出选择时,会出现非决定论。在CSP的形式代数中,这是数学建模的。幸运的是,你不需要知道数学就能使用围棋。但从形式上讲,两个goroutines代码并行执行,如果消除了所有选择,结果仍然是确定的。

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

公平饥饿通常是同一枚硬币的反面。饥饿是一种动态问题(以及死锁、活动锁定和比赛条件),可能会导致糟糕的表现,更可能导致错误的行为。这些动态问题是不可测试的(更多关于这个),需要一些级别的分析来解决。显然,如果系统的一部分因为缺乏对某些资源的访问而没有响应,那么就需要在管理这些资源时更加公平。

由于当前的FIFO行为,对信道端的共享访问可以很好地提供一定程度的公平性,并且这可能看起来足够了。但是,如果您希望它得到保证(不管实现的不确定性如何),那么可以在阵列中使用select和一束点对点通道。通过总是按照将最后一个选定的放在最底部的顺序对它们进行偏好,可以很容易地实现公平索引。此解决方案可以保证的公平性,但可能会带来较小的性能损失。

(顺便说一句:英国坎特伯雷的研究人员就Java虚拟机中的一个公平缺陷做出了一个有趣的发现,这一发现从未得到纠正!)

我认为这是未指定的,因为内存模型文档只说"在通道上的发送发生在该通道的相应接收完成之前。"关于send语句和receive运算符的规范部分没有说明首先取消阻止的内容。现在gc工具链使用一个有序的FIFO队列来控制哪个goroutine取消阻塞,但我在规范中没有看到任何承诺,它必须总是这样。

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

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

权威文献是Go Memory模型。内存模型没有为发送到同一通道的两个goroutine定义先发生后发生的关系,因此没有指定顺序。同上,用于接收。

最新更新