Как сделать идиоматическую синхронизацию со временем. После? - PullRequest
1 голос
/ 14 апреля 2019

Я пишу приложение, которое ставит в очередь входящие запросы.Если запрос находился в очереди более определенного времени, я бы хотел выбросить тайм-аут.Я делаю это со временем. После:

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, но для bool еще не установлено значение true!

Существует ли идиоматический способ решения проблемы синхронизации такого рода в Go?

1 Ответ

3 голосов
/ 14 апреля 2019

Использовать обработку координат мьютекса данных и тайм-аут.

Определить тип для хранения мьютекса, ввода, результата, канала, сигнализирующего о завершении работы, и флага, указывающего, что работа, если таковая имеется, завершено.

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()
}

Чтобы убедиться, чточто обработчик запроса ожидает результата, процессор очереди удерживает блокировку при обработке рабочего элемента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...