Go 中通过通道传递的消息是否保证是非阻塞的?
为了评估 go 是否是音频/视频应用程序的可能选项,我想知道 go 中传递的消息是否满足任何非阻塞进度保证(无阻塞、无锁或无等待)。特别是,以下场景是相关的:
单个生产者单个消费者:
两个线程使用共享通道进行通信。线程A只做异步发送,线程B只做异步接收。假设操作系统调度程序决定在“最糟糕的时刻”无限期地中断线程 A。线程 B 是否保证在有限数量的 cpu 周期内完成接收操作,或者线程 A 是否有(理论上)可能将通道置于线程 B 需要等待操作系统恢复线程 A 的状态?
多个生产者:
多个线程 A1、A2、A3...使用共享通道与一个或多个其他线程进行通信。线程 Ai 仅执行异步发送。假设 A2、A3...在“最糟糕的时刻”被操作系统调度程序无限期地挂起。线程 A1 是否仍能保证在有限数量的 cpu 周期内完成发送操作?进一步假设每个线程只想发送一次。如果程序运行足够长的时间(使用“恶意”调度程序可能会导致某些线程挨饿或在“最糟糕的时刻”中断并恢复线程),是否至少可以保证一次发送成功?
我对这里的典型场景不太感兴趣,而是对最坏情况的保证感兴趣。 有关阻塞、锁定和等待的更多详细信息,请参阅非阻塞算法(维基百科) -免费算法。
In order to assess whether go is a possible option for an audio/video application, I would like to know whether message passing in go satisfies any non-blocking progress guarantees (being obstruction-free, lock-free or wait-free). In particular, the following scenarios are relevant:
Single producer single consumer:
Two threads communicate using a shared channel. Thread A only does asynchronous sends, thread B only does asynchronous receives. Suppose the OS scheduler decides to interrupt thread A at the "worst possible moment" for an indefinite amount of time. Is thread B guaranteed to finish a receive operation in a bounded number of cpu cycles or is there a (theoretical) possibility that thread A can put the channel into a state where thread B needs to wait for the OS to resume thread A?
Multiple producers:
Several threads A1, A2, A3, ... communicate with one or more others threads using a shared channel. The threads Ai only do asynchronous sends. Suppose A2, A3, ... are suspended by the OS scheduler at the "worst possible moment" for an indefinite amount of time. Is thread A1 still guaranteed to finish a send operation in a bounded number of cpu cycles? Suppose further that each thread only wants to do one send. If the program is run sufficiently long (with a "malicious" scheduler which potentially starves some threads or interrupts and resumes threads at the "worst possible moment"), is at least one send guaranteed to succeed?
I am not so much interested in typical scenarios here, but rather worst-case guarantees.
See Non-blocking algorithm (Wikipedia) for more details on obstruction-, lock- and wait-free algorithms.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
根据定义,正常发送和接收是阻塞操作。您可以使用 select 语句进行非阻塞发送或接收:(
接收非常相似;只需替换 case 语句。)
仅当通道缓冲区中有空间时才会发生发送。否则运行默认情况。请注意,内部仍然使用互斥锁(如果我正确阅读代码)。
Normal sends and receives are blocking operations by definition. You can do a non-blocking send or receive by using a select statement:
(Receiving is very similar; just replace the case statement.)
The send only takes place when there is room in the channel's buffer. Otherwise the default case runs. Note that internally a mutex is still used (if I'm reading the code correctly).
Go内存模型不要求发送和接收是非阻塞的,并且当前的< a href="http://golang.org/src/pkg/runtime/chan.c#L173" rel="nofollow">运行时 实现锁定
send
和的通道接收
。这意味着,例如,如果操作系统调度程序中断正在运行另一个 go 例程的另一个线程,而该线程在已获取通道锁的情况下尝试在同一通道上发送或接收,则可能会导致发送或接收 go 例程挨饿。 。所以不幸的是答案是否:(
(除非有人使用非阻塞算法重新实现部分运行时)。
The Go Memory Model doesn't require sends and receives to be non-blocking, and the current runtime implementation locks the channel for
send
andrecv
. This means, for instance, that it is possible to starve a sending or receiving go-routine if the OS-scheduler interrupts another thread running another go-routine which tries to send or receive on the same channel while it has already acquired the channel's lock.So the answer is unfortunately no :(
(unless someone reimplement parts of the runtime using non-blocking algorithms).
您询问是否保证一个操作在有限数量的周期内完成,这当然不是该语言(或大多数底层操作系统)的设计考虑因素。
如果在单线程中运行,则 Go 在 goroutine 之间使用协作多任务。因此,如果一个例程永远不会屈服,那么另一个例程将永远不会运行。如果您的程序在多个线程上运行(由 GOMAXPROCS 设置),那么您可以同时运行多个 goroutine,在这种情况下,操作系统控制线程之间的调度。然而,在这两种情况下,函数调用的完成时间都没有保证上限。
请注意,goroutine 的协作性质使您可以对调度执行进行一定程度的控制——也就是说,例程永远不会被抢占。在您屈服之前,您将保留对线程的控制权。
至于阻塞行为,请参阅语言规范:
请注意,通道上的非阻塞发送和接收可以使用已经提到的
select
语法来完成。You're asking whether an operation is guarantee to complete within a bounded number of cycles, which of course is not a design consideration for this language (or most underlying OSes).
If run in a single thread, then Go uses cooperative multitasking between goroutines. So if one routine never yields, then the other will never run. If your program runs on multiple threads (as set by
GOMAXPROCS
), then you can run several goroutines simultaneously, in which case the OS controls scheduling between the threads. However, in neither case is there a guaranteed upper bound on the time to completion for a function call.Note that the cooperative nature of goroutines gives you some amount of control over scheduling execution -- that is, routines are never preempted. Until you yield, you retain control of the thread.
As for blocking behavior, see The language specification:
Note that non-blocking sends and receives on channels can be accomplished using the
select
syntax already mentioned.Goroutine 不拥有通道或通道上发送的值。因此,已经在通道上发送/正在发送值的 Goroutine 的执行状态不会影响其他 Goroutine 在该通道上发送或接收值的能力,除非通道的缓冲区已满,在这种情况下所有发送都会阻塞直到发生相应的接收,或者缓冲区为空,在这种情况下,所有接收都将阻塞,直到有相应的发送。
因为 goroutine 使用协作调度(它们必须屈服于调度程序,要么通过通道操作、系统调用,要么显式调用
runtime.Gosched()
),所以 goroutine 不可能在“最糟糕的时刻”被打断。 Goroutine 有可能永远不会屈服,在这种情况下,它可能会无限期地占用线程。如果你只有一个执行线程,那么你的其他 goroutine 将永远不会被调度。 Goroutine 永远不会被调度是可能的,但从统计角度来看是不可能的。然而,如果除了一个 Goroutine 之外的所有 Goroutine 在发送或接收时都被阻塞,则必须调度剩余的 Goroutine。有可能陷入僵局。如果我有两个 goroutine 正在执行:
那么,很明显,两个 goroutine 都在等待对方发送一个值,而这永远不会发生。
Goroutines do not own channels or the values sent on them. So, the execution status of a goroutine that has sent / is sending values on a channel has no impact on the ability of other goroutines to send or receive values on that channel, unless the channel's buffer is full, in which case all sends will block until a corresponding receive occurs, or the buffer is empty, in which case all receives will block until there is a corresponding send.
Because goroutines use cooperative scheduling (they have to yield to the scheduler, either through a channel operation, a syscall, or an explicit call to
runtime.Gosched()
), it is impossible for a goroutine to be interrupted at the "worst possible time". It is possible for a goroutine to never yield, in which case, it could tie up a thread indefinitely. If you have only one thread of execution, then your other goroutines will never be scheduled. It is possible, but statistically improbable, for a goroutine to just never be scheduled. However, if all goroutines but one are blocked on sends or receives, then the remaining goroutine must be scheduled.It is possible to get a deadlock. If I have two goroutines executing:
Then, as should be apparent, both goroutines are waiting for the other to send a value, which will never happen.