9

为了评估 go 是否是音频/视频应用程序的可能选项,我想知道传入 go 的消息是否满足任何非阻塞进度保证(无阻塞、无锁或无等待)。特别是,以下情况是相关的:

单一生产者单一消费者:

两个线程使用共享通道进行通信。线程 A 只做异步发送,线程 B 只做异步接收。假设 OS 调度程序决定在“可能的最坏时刻”无限期地中断线程 A。线程 B 是否保证在有限数量的 cpu 周期内完成接收操作,或者线程 A 是否有(理论上)可能将通道置于线程 B 需要等待操作系统恢复线程 A 的状态?

多个生产者:

多个线程 A1、A2、A3、... 使用共享通道与一个或多个其他线程通信。线程 Ai 只进行异步发送。假设 A2、A3、... 在“最坏的可能时刻”被 OS 调度程序挂起无限时间。线程 A1 是否仍然保证在有限的 cpu 周期内完成发送操作?进一步假设每个线程只想进行一次发送。如果程序运行的时间足够长(使用“恶意”调度程序,它可能会饿死一些线程或中断并在“最糟糕的时刻”恢复线程),是否至少有一个发送保证成功?

我对这里的典型场景不太感兴趣,而是对最坏情况的保证感兴趣。有关无阻塞、无锁和无等待算法的更多详细信息,请参阅非阻塞算法(维基百科) 。

4

4 回答 4

11

根据定义,正常的发送和接收是阻塞操作。您可以使用 select 语句进行非阻塞发送或接收:

select {
case ch <- msg:
default:
}

(接收非常相似;只需替换 case 语句即可。)

仅当通道缓冲区中有空间时才会进行发送。否则默认情况下运行。请注意,内部仍然使用互斥锁(如果我正确阅读代码)。

于 2011-07-17T20:45:37.297 回答
4

Go 内存模型不要求发送和接收是非阻塞的,并且当前的运行时 实现send为和锁定通道recv。这意味着,例如,如果 OS 调度程序中断另一个运行另一个 go-routine 的线程,该线程试图在同一通道上发送或接收,而它已经获得了通道的锁,则可能会使发送或接收 go-routine 饿死.

所以不幸的是答案是否定的:(

(除非有人使用非阻塞算法重新实现部分运行时)。

于 2012-04-15T23:18:50.883 回答
1

您是在询问一个操作是否保证在有限的周期内完成,这当然不是这种语言(或大多数底层操作系统)的设计考虑因素。

如果在单线程中运行,那么 Go在 goroutine 之间使用协作多任务。因此,如果一个例程永远不会产生,那么另一个例程将永远不会运行。如果您的程序在多个线程上运行(由 设置GOMAXPROCS),那么您可以同时运行多个 goroutine,在这种情况下,操作系统控制线程之间的调度。但是,在这两种情况下,函数调用的完成时间都没有保证上限。

请注意,goroutine 的协作特性使您可以对调度执行进行一定程度的控制——也就是说,例程永远不会被抢占。在您屈服之前,您保留对线程的控制。

至于阻塞行为,请参阅语言规范

容量(以元素数量计)设置通道中缓冲区的大小。如果容量大于零,则通道是异步的:如果缓冲区未满(发送)或非空(接收),则通信操作成功而不会阻塞,并且元素按发送顺序接收。如果容量为零或不存在,则仅当发送方和接收方都准备好时,通信才会成功。

select请注意,可以使用已经提到的语法来完成通道上的非阻塞发送和接收。

于 2011-07-18T17:40:09.353 回答
0

Goroutine 不拥有通道或在通道上发送的值。因此,一个已经发送/正在发送值的 goroutine 的执行状态不会影响其他 goroutine 在该通道上发送或接收值的能力,除非通道的缓冲区已满,在这种情况下所有发送都会阻塞直到发生相应的接收,或者缓冲区为空,在这种情况下,所有接收都将阻塞,直到有相应的发送。

因为 goroutines 使用协作调度(它们必须通过通道操作、系统调用或显式调用来屈服于调度程序runtime.Gosched()),所以 goroutine 不可能在“最坏的可能时间”被中断。goroutine 有可能永远不会屈服,在这种情况下,它可能会无限期地占用一个线程。如果您只有一个执行线程,那么您的其他 goroutine 将永远不会被调度。一个 goroutine 永远不会被调度是可能的,但在统计上是不可能的。但是,如果除一个之外的所有 goroutine 在发送或接收时都被阻塞,则必须调度剩余的 goroutine。

有可能陷入僵局。如果我有两个 goroutine 正在执行:

func Goroutine(ch1, ch2 chan int) {
   i := <-ch1
   ch2 <- i
}
...
ch1, ch2 := make(chan int), make(chan int)
go Goroutine(ch1, ch2)
go Goroutine(ch2, ch1)

然后,应该很明显,两个 goroutine 都在等待另一个发送值,这永远不会发生。

于 2011-07-20T03:37:58.777 回答