简体   繁体   English

单通道上Goroutine解锁的顺序

[英]Order of Goroutine Unblocking on Single Channel

Does order in which the Goroutines block on a channel determine the order they will unblock? Goroutines在频道上阻止的顺序是否决定了它们将解锁的顺序? I'm not concerned with the order of the messages that are sent (they're guaranteed to be ordered), but the order of the Goroutines that'll unblock. 我并不关心发送的消息的顺序(保证它们是有序的),而是Goroutines的顺序,它将解锁。

Imagine a empty Channel ch shared between multiple Goroutines (1, 2, and 3), with each Goroutine trying to receive a message on ch . 想象一下在多个Goroutines(1,2和3)之间共享一个空的Channel ch ,每个Goroutine试图在ch上接收一条消息。 Since ch is empty, each Goroutine will block. 由于ch是空的,每个Goroutine都会阻塞。 When I send a message to ch , will Goroutine 1 unblock first? 当我向ch发送消息时,Goroutine 1会先解锁吗? Or could 2 or 3 possibly receive the first message? 或者2或3可能会收到第一条消息? (Or vice-versa, with the Goroutines trying to send) (反之亦然,Goroutines试图发送)

I have a playground that seems to suggest that the order in which Goroutines block is the order in which they are unblocked, but I'm not sure if this is an undefined behavior because of the implementation. 我有一个游乐场似乎表明Goroutines阻止的顺序是它们被解锁的顺序,但我不确定这是否是一个未定义的行为,因为实现。

This is a good question - it touches on some important issues when doing concurrent design. 这是一个很好的问题 - 在进行并行设计时涉及一些重要问题。 As has already been stated, the answer to your specific question is, according to the current implementation, FIFO based. 如前所述,根据当前的实施,您的具体问题的答案是基于FIFO。 It's unlikely ever to be different, except perhaps if the implementers decided, say, a LIFO was better for some reason. 它可能不会有所不同,除非实施者决定,例如,LIFO由于某种原因更好。

There is no guarantee , though. 但是, 不能保证 So you should avoid creating code that relies on a particular implementation. 因此,您应该避免创建依赖于特定实现的代码。

The broader question concerns non-determinism , fairness and starvation . 更广泛的问题涉及非决定论公平饥饿

Perhaps surprisingly, non-determinism in a CSP-based system does not come from things happening in parallel. 也许令人惊讶的是,基于CSP的系统中的非确定性并非来自并行发生的事情。 It is possible because of concurrency, but not because of concurrency. 可能是因为并发,但不是因为并发。 Instead, non-determinism arises when a choice is made . 相反, 当做出选择时会产生非决定论 In the formal algebra of CSP, this is modelled mathematically. 在CSP的正式代数中,这是以数学方式建模的。 Fortunately, you don't need to know the maths to be able to use Go. 幸运的是,您不需要知道能够使用Go的数学。 But formally, two goroutines code execute in parallel and the outcome could still be deterministic, provided all the choices are eliminated. 但正式地说,两个goroutines代码并行执行,如果消除了所有选择,结果仍然可以是确定性的。

Go allows choices that introduce non-determinism explicitly via select and implicitly via ends of channels being shared between goroutines. Go允许通过select显式引入非确定性的select并通过goroutines之间共享的通道的末端隐式引入。 If you have point-to-point (one reader, one writer) channels, the second kind does not arise. 如果您有点对点(一个阅读器,一个写入器)通道,则不会出现第二种通道。 So if it's important in a particular situation, you have a design choice you can make. 因此,如果它在特定情况下很重要,那么您可以选择设计。

Fairness and starvation are typically opposite sides of the same coin. 公平饥饿通常是同一枚硬币的对立面。 Starvation is one of those dynamic problems (along with deadlock, livelock and race conditions) that result perhaps in poor performance, more likely in wrong behaviour. 饥饿是那些动态问题(以及死锁,活锁和竞争条件)之一,可能导致表现不佳,更有可能出现错误行为。 These dynamic problems are un-testable ( more on this ) and need some level analysis to solve. 这些动态问题是不可测试的( 更多内容 ),需要进行一些级别分析才能解决。 Clearly, if part of a system is unresponsive because it is starved of access to certain resources, then there is a need for greater fairness in governing those resources. 显然,如果系统的一部分没有响应,因为它缺乏对某些资源的访问权限,那么在管理这些资源方面需要更加公平。

Shared access to channel ends may well provide a degree of fairness because of the current FIFO behaviour and this may appear sufficient. 由于当前的FIFO行为,对信道端的共享访问可能提供一定程度的公平性,这可能看起来足够了。 But if you want it guaranteed (regardless of implementation uncertainties), it is possible instead to use a select and a bundle of point-to-point channels in an array. 但是如果你想保证它(不管实现的不确定性),可以在数组中使用select和一组点对点通道。 Fair indexing is easy to achieve by always preferring them in an order that puts the last-selected at the bottom of the pile. 公平的索引很容易实现,总是按照将最后选择放在堆底部的顺序来选择它们。 This solution can guarantee fairness, but probably with a small performance penalty. 这种解决方案可以保证公平性,但可能会带来很小的性能损失。

(aside: see "Wot No Chickens" for a somewhat-amusing discovery made by researchers in Canterbury, UK concerning a fairness flaw in the Java Virtual Machine - which has never been rectified!) (旁边:看看“Wot No Chickens”,因为英国坎特伯雷的研究人员发现了一个有趣的发现,关于Java虚拟机的公平性缺陷 - 从未纠正过!)

I believe it's unspecified because the memory model document only says "A send on a channel happens before the corresponding receive from that channel completes." 我相信它没有具体说明,因为内存模型文档只说“在该通道的相应接收完成之前发生了一个通道上的发送”。 The spec sections on send statements and the receive operator don't say anything about what unblocks first. send语句接收运算符的规范部分没有说明首先解除阻塞的内容。 Right now the gc toolchain uses an orderly FIFO queue to control which goroutine unblocks, but I don't see any promises in the spec that it must always be so. 现在,gc工具链使用一个有序的FIFO队列来控制哪个goroutine解除阻塞,但我没有在规范中看到它必须始终如此的任何承诺。

(Just for general background note that Playground code runs with GOMAXPROCS=1, ie, on one core, so some types of concurrency-related unpredictability just won't come up.) (仅仅是为了一般背景说明,Playground代码在GOMAXPROCS = 1的情况下运行,即在一个核心上运行,因此某些类型的并发相关的不可预测性不会出现。)

The order is not specified, but current implementations use a FIFO queue for waiting goroutines. 未指定顺序,但当前实现使用FIFO队列来等待goroutine。

The authoritative document is the Go Memory Model . 权威文件是Go Memory Model The memory model does not define a happens-before relationship for two goroutines sending to the same channel, therefore the order is not specified. 内存模型没有为发送到同一通道的两个goroutine定义发生在之前的关系,因此未指定顺序。 Ditto for receive. 同上接收。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM