Go за Прикладом: Тайм-аути (Timeouts)

Тайм-аути необхідні програмам, що тісно взаємодіють з зовнішніми ресурсами або враховують власний час виконання. Завдяки каналам та select - робота з тайм-аутами в Go - проста і приємна.

package main
import (
    "fmt"
    "time"
)

Припустимо, в нашому прикладі, ми виконуємо виклик кудись “назовні”, що поверне результат каналом c1 за 2 секунди.

func main() {
    c1 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c1 <- "результат 1"
    }()

Використання select реалізує таймаут, де res := <-c1 чекає на повідомлення і <-Time.After також чекає (1 секунду), на повідомлення з каналу (який сам і створює). Оскільки select опрацьовує лише перший готовий виклик, переможцем буде time.After оскільки його виконання займає, лише, 1 секунду, на противагу 2-ом секундам виконання повільного запиту “назовні” (відповідь з якого очікуємо з c1).

    select {
    case res := <-c1:
        fmt.Println(res)
    case <-time.After(1 * time.Second):
        fmt.Println("таймаут 1")
    }

Якщо ми зробимо тайм-аут трошки довшим (скажімо - 3 секунди), ми встигнемо отримати та надрукувати результат каналу c2, що буде отримано раніше більш довгого тайм-ауту.

    c2 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "результат 2"
    }()
    select {
    case res := <-c2:
        fmt.Println(res)
    case <-time.After(3 * time.Second):
        fmt.Println("таймаут 2")
    }
}

Запуск цієї програми покаже що підчас першої операції час сплив, а друга операцію завершилась вчасно.

$ go run timeouts.go
таймаут 1
результат 2

Використання цього шоблону напряму залежить від обміну результатами за допомогою каналів. Викорастання канали, поряд з select - є основою Go в реалізації ассинхроності. Надалі ми розглянемо ще два приклади, що використовують данні примітиви: хронометри та маятники.

Наступний приклад: Не Блокуючі Операції на Каналах.