【问题标题】:How to do idiomatic synchronization with time.After?如何与 time.After 进行惯用同步?
【发布时间】:2019-04-13 21:52:06
【问题描述】:

我正在编写一个对传入请求进行排队的应用程序。如果一个请求已经在队列中超过了一定的时间,我想抛出一个超时。我正在做这与时间。之后:

timeoutCh := time.After(5 * time.Second)
select {
    case <-timeoutCh:
         //throw timeout 504
    case <-processing:
         //process request
}

处理通道(连同请求)被放入队列中,当一个请求被取出处理时,我向通道发送一个信号来命中case语句:

processing <- true

这样做的问题是如果已经选择了 timeoutCh,处理通道会阻塞,所以我需要一些方法来检查请求是否超时。

我考虑过使用共享原子布尔值,但如果我这样做:

case <-timeoutCh:
     requestTimedOut = true

然后在发送到处理通道之前检查布尔值,仍然存在竞争条件,因为可能已经选择了 timeoutCh 情况,但布尔值尚未设置为 true!

有没有一种惯用的方式来处理 Go 中的这种同步问题?

【问题讨论】:

  • 这是您要解决的更高级别的问题吗:从队列中删除请求时,如果请求在队列中的时间超过某个指定的持续时间,则丢弃该请求并报错。跨度>
  • 是的——这就是我要解决的问题!
  • 使用struct { r Request, enqueueTime time.Time }作为队列元素类型并在从队列中删除元素时检查入队时间是否有效? if time.Since(e.enqueueTime) &gt; duration { /* discard with error */ }
  • 这几乎可以工作,只是一旦超过超时就不会返回 504。
  • 如何处理双向通道并从选择中的超时块发送信号?这样,您的队列可以通过检查&lt;-processing 来丢弃请求。或者有一个专门用于该用途的单独频道,例如hasTimedOut。 (编辑:我想我误解了你的问题忽略我)

标签: go synchronization timeout


【解决方案1】:

使用互斥量坐标处理数据和超时。

定义一个类型来保存互斥体、输入、结果、一个表示工作完成的通道和一个指示工作(如果有的话)已经完成的标志。

type work struct {
    sync.Mutex
    input    InputType
    result   ResultType
    signal   chan struct {}
    done     bool
}

请求处理程序创建一个工作项并将其排入队列,并等待超时或来自队列处理器的信号。无论哪种方式,请求处理程序都会检查队列处理器是否完成了工作并做出适当的响应。

func handler(resp http.ResponseWriter, req *http.Request) {
    w := &queueElement{
        input: computeInputFromRequest(req)
        signal:  make(chan struct{})
    }
    enqueue(w)

    // Wait for timeout or for queue processor to signal that the work is complete.
    select {
    case <-time.After(5 * time.Second):
    case <-w.signal:
    }

    w.Lock()
    done := w.done  // Record state of the work item.
    w.done = true   // Mark the work item as complete.
    w.Unlock()

    if !done {
        http.Error(w, "Timeout", http.StatusGatewayTimeout)
    }  else {
        respondWithResult(resp, w.result)
    }
}

队列处理器看起来像这样:

 for {
   w := dequeue()
   w.Lock()
   if !w.done {
      w.done = true
      w.result = computeResultFromInput(w.input)
      close(w.signal)
   }
   w.Unlock()
}

为确保请求处理程序等待结果,队列处理器在处理工作项时持有锁。

【讨论】:

  • 我的理解是 Go 不保证 goroutine 的运行顺序。所以,假设,如果我们检查上下文,它没有被取消。然后,time.After 触发,我们返回一个 504。接下来,我们发送到 resultCh。还有一个比赛条件,对吧?从队列中读取的内容无法绝对确定我们还没有提供 504。也许我遗漏了一些东西。
  • 在请求处理程序返回后队列可能会处理工作,这是一种竞争。没有数据竞争或竞争会导致 goroutine 永远阻塞。
  • 嗯,但是使用共享原子布尔值也是如此,就像我在问题中提到的那样,对吧?我要解决的主要问题是确保在我们返回 504 时不会发生处理。
  • 看起来很棒——感谢你的努力——真的很有帮助!
猜你喜欢
  • 1970-01-01
  • 2016-04-19
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-09
  • 2014-11-13
相关资源
最近更新 更多