Golang прерывистое поведение на тайм-ауте Goroutine - PullRequest
0 голосов
/ 10 июля 2019

Я пытаюсь реализовать параллелизм для повторяющейся задачи. Я хочу реализовать http-запрос на другом Goroutine (изображен функцией longRunningTask). Я предоставляю таймер для механизма, чтобы остановить Goroutine, и отправляет сигнал тайм-аута на главный Goroutine, если задание с большой нагрузкой продолжает заданное время ожидания. Проблема, с которой я сталкиваюсь в настоящее время, заключается в том, что у меня прерывистое поведение.

Код был упрощен, чтобы выглядеть как показано ниже.

package main

import (
    "fmt"
    "time"
)

func main() {
    var iteration int = 5

    timeOutChan := make(chan struct{})
    resultChan := make(chan string)

    for i := 0; i < iteration; i++ {
        go longRunningTaks(timeOutChan, resultChan)
    }

    for i := 0; i < iteration; i++ {
        select {
        case data := <-resultChan:
            fmt.Println(data)
        case <-timeOutChan:
            fmt.Println("timed out")
        }
    }

}

func longRunningTaks(tc chan struct{}, rc chan string) {
    timer := time.NewTimer(time.Nanosecond * 1)
    defer timer.Stop()

    // Heavy load task
    time.Sleep(time.Second * 1)

    select {
    case <-timer.C:
        tc <- struct{}{}
    case rc <- "success":
        return
    }
}

Я считаю, что все попытки должны быть распечатаны

timeout
timeout
timeout
timeout
timeout

Вместо этого я получил прерывистый

success
timeout
timeout
timeout
timeout

1 Ответ

0 голосов
/ 10 июля 2019

В документе упоминается:

NewTimer создает новый таймер, который будет отправлять текущее время на своем канале после как минимум продолжительности d .

«по крайней мере, означает», что таймер наверняка займет определенное время, однако это также неявно означает, что может занять больше времени, чем указано.Таймер запускает свою собственную процедуру запуска и записывает канал по истечении срока действия.Из-за планировщика или сборки мусора или процессов записи в другой канал могут быть отложены.Кроме того, смоделированная рабочая нагрузка очень коротка, учитывая вышеописанные возможности.

Обновление :

Как отметил Питер в комментарии, написание "успеха" для канала rc - это действие, которое в равной степени вероятнозавершено, потому что это может быть прочитано с другого конца основной процедурой.Выбор должен выбирать между 1) записью «успеха» в канал rc и 2) истекшим таймером.И оба возможны.Вероятность No1 больше в начале, потому что основной рутине еще предстоит прочитать ее с другого конца.Как только это произойдет.Другие оставшиеся подпрограммы должны будут конкурировать за канал (чтобы написать "success") (поскольку он блокируется с размером буфера 0), поэтому в остальное время вероятность выбора таймера с истекшим сроком действия больше, чем не можетскажите, с какой скоростью будет прочитана основная процедура из канала resultChan (другой конец rc).

...