如何确定go频道的哪一侧正在等待?

时间:2016-12-27 23:33:29

标签: performance go concurrency channel

如何确定go通道的哪一侧正在另一侧等待?

我想知道这一点,所以我可以弄清楚我的处理受到限制的地方,并通过分配更多资源做出回应。

一些选项

我想到的两种方法都需要做一些记录值的移动平均值,因此测量值不会太嘈杂,但这不是big problem

  1. 使用计时器检查在消费者中等待的时间百分比
  2. 在单个消费者的情况下,我可以在从频道消费之前启动计时器,在我获得记录后停止计时器。我可以跟踪等待的时间百分比,并在每个获取周期内做出相应的响应。

    1. 缓冲通道的样本长度
    2. 如果频道定期为0,则意味着我们比发送消费更快。同样,如果缓冲区已满,我们发送的速度比我们收到的速度快。我们可以检查我们频道的长度,以确定运行缓慢的内容。

      出于性能原因或其他原因,是否有充分的理由选择其中一个到另一个?这个问题有更简单的解决方案吗?

      实施例

      我的服务正在执行N个HTTP请求,同时抓取最多W个goroutines中的内容,并将频道上的所有内容发送到processor个单个goroutine,然后将数据反馈给客户。

      每个工作人员任务都会导致在频道上发送大量消息。每个worker的任务可能需要几分钟才能完成。

      下图总结了3个并发工作者(W=3)的数据流。

          [worker: task 1] -
                            \
          [worker: task 2] - | --- [ channel ] --- [ processor ] -> [ client ]
                            /
          [worker: task 3] -
      

      我想知道在请求期间是否应该运行更多工作人员(增加W)或更少工人(减少W)。由于客户端在速度非常不同的连接上工作,因此每个请求的变化很大。

1 个答案:

答案 0 :(得分:3)

实现目标的一种方法是使用“有界发送”和“有界接收”操作 - 如果您能够提出合理的轮询超时。

当你的任何一个工人试图通过频道发送一个完整的结果时,不要让它“永远”阻塞(直到频道的缓冲区中有空格);相反,只允许它阻止一些最长的时间。如果在通道缓冲区中有空间之前发生超时,则可以对该条件做出反应:计算它发生的次数,调整未来的最后期限,限制或减少工人数量,等等。

同样,对于接收工作人员结果的“处理器”,您可以限制其阻止的时间。如果在有可用值之前发生超时,则处理器将处于饥饿状态。创建更多的工人来更快地提供它(假设工人将受益于这种并行性)。

这种方法的缺点是每个creating timerssendreceive operation开销。

草绘,每个工人都可以访问这些声明:

const minWorkers = 3
var workers uint32

在每个工人goroutine中:

atomic.AddUint32(&workers, 1)
for {
    result, ok := produce()
    if !ok {
        break
    }
    // Detect when channel "p"'s buffer is full.
    select {
    case p <- result:
    case <-time.After(500 * time.Millisecond):
        // Hand over the pending result, no matter how long it takes.
        p <- result
        // Reduce worker count if above minimum.
        if current := atomic.LoadUint32(&workers); current > minWorkers &&
            atomic.CompareAndSwapUint32(&workers, current, current-1) {
            return
        }
        // Consider whether to try decrementing the working count again
        // if we're still above the minimum. It's possible another one
        // of the workers also exited voluntarily, changing the count.
    }
}
atomic.AddUint32(&workers, -1)

请注意,如上所述,您可以通过计算发送到通道p完成所需的时间以及花费太长时间对其做出反应来实现相同的效果,而不是执行一次有限发送然后是潜在的阻止发送。但是,我用这种方式草拟了它,因为我怀疑这样的代码会在超时到期时成熟,包括日志记录和检测计数器颠簸。

同样,在处理器goroutine中,您可以限制阻止从工作人员接收值的时间:

for {
    select {
    case result <- p:
        consume(result)
    case <-time.After(500 * time.Millisecond):
        maybeStartAnotherWorker()
    }
}

显然,你可以在这个装置上加上许多旋钮。最终将生产者的调度与消费者和生产者本身联系起来。引入一个不透明的“倾听者”,生产者和消费者“抱怨”延迟可以让你打破这种循环关系,更容易改变控制你如何应对拥堵的政策。