Go на примерах: Таймауты

Таймауты важны для программ, которые подключаются к внешним ресурсам или которым нужно ограничить время выполнения. Реализовать таймауты в Go легко и элегантно благодаря каналам и select.

package main
import (
    "fmt"
    "time"
)
func main() {

Допустим, мы выполняем внешний вызов, который возвращает результат в канал c1 через 2 секунды. Обрати внимание, что канал буферизованный, поэтому отправка в горутине неблокирующая. Это распространённый паттерн для предотвращения утечки горутин в случае, если из канала никогда не прочитают.

    c1 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c1 <- "result 1"
    }()

Вот select, реализующий таймаут. res := <-c1 ожидает результат, а <-time.After ожидает значение, которое будет отправлено после таймаута в 1 секунду. Поскольку select выполняет первое готовое получение, мы попадём в случай таймаута, если операция займёт больше разрешённой 1 секунды.

    select {
    case res := <-c1:
        fmt.Println(res)
    case <-time.After(1 * time.Second):
        fmt.Println("timeout 1")
    }

Если мы установим более длинный таймаут в 3 секунды, то получение из c2 успеет выполниться и мы выведем результат.

    c2 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "result 2"
    }()
    select {
    case res := <-c2:
        fmt.Println(res)
    case <-time.After(3 * time.Second):
        fmt.Println("timeout 2")
    }
}

Запуск этой программы показывает, что первая операция завершилась по таймауту, а вторая успешно выполнилась.

$ go run timeouts.go 
timeout 1
result 2

Далее: .