【问题标题】:Order of Goroutine Unblocking on Single Channel单通道上的 Goroutine 解锁顺序
【发布时间】:2014-11-09 17:20:36
【问题描述】:

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

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

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

【问题讨论】:

    标签: go channel


    【解决方案1】:

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

    (仅作为一般背景说明,Playground 代码以 GOMAXPROCS=1 运行,即在一个内核上运行,因此不会出现某些类型的与并发相关的不可预测性。)

    【讨论】:

      【解决方案2】:

      这是一个很好的问题——它涉及到并发设计时的一些重要问题。如前所述,根据当前的实现,您的具体问题的答案是基于 FIFO 的。它不太可能永远不同,除非实施者决定,比如说,后进先出法更好。

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

      更广泛的问题涉及非确定性公平饥饿

      也许令人惊讶的是,基于 CSP 的系统中的非确定性并非来自并行发生的事情。 可能是因为并发,但不是因为是并发。相反,在做出选择时会出现不确定性。在 CSP 的形式代数中,这是在数学上建模的。幸运的是,您无需了解数学即可使用 Go。但在形式上,两个 goroutine 代码是并行执行的,只要排除所有选择,结果仍然可以确定。

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

      公平饥饿通常是同一枚硬币的两面。饥饿是那些可能导致性能不佳,更有可能导致错误行为的动态问题之一(以及死锁、活锁和竞争条件)。这些动态问题是不可测试的 (more on this),需要一些水平分析来解决。显然,如果系统的一部分因为无法访问某些资源而没有响应,那么在管理这些资源时需要更加公平。

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

      (除此之外:请参阅"Wot No Chickens",了解英国坎特伯雷的研究人员关于 Java 虚拟机中的公平性缺陷的一些有趣的发现 - 该缺陷从未得到纠正!)

      【讨论】:

      • 合法的很酷的帖子,但总的来说,我不会鼓励人们继续使用select 编写应用程序土地保证公平调度程序,除非他们真的必须这样做。跨度>
      • 你是对的,简单的共享频道更容易 - 因此更不容易出错 - 我也鼓励人们正常使用它。但是,如果需要可以使用select,这并没有什么坏处,并且知道如何进行公平的选择也是一项有用的技能。
      • 这就像有人问映射键是否有定义的顺序,我们自愿回应他们可以实现您自己的哈希映射。他们能!但首先他们需要知道内置函数对他们真的不起作用,以及他们将如何做得更好。鼓励自定义调度将导致许多没有实际调度程序的人中的一些人写出可能实际上会受到伤害的东西。
      • 无论如何,如果我正在写这篇文章,我会注意到应用程序空间调度程序的可能性,但不鼓励这样做,直到他们看到它解决的问题,并且不会称之为“保证”公平调度”(因为这让人们想到,例如,内核调度程序可以提供更强的等时保证,因为它们有更多的控制权)。
      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-08
        • 2022-01-16
        • 2019-05-04
        • 1970-01-01
        • 2020-03-18
        • 2018-08-12
        相关资源
        最近更新 更多