Go با مثال: مهلت زمانی (Timeout)

مهلت زمانی (timeout) برای برنامه‌هایی که به منابع خارجی وصل می‌شوند یا به هر دلیلی باید زمان اجرا را محدود کنند اهمیت دارد. پیاده‌سازی timeout در Go به لطف کانال‌ها و select ساده و زیباست.

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

در این مثال فرض کنید یک فراخوانی خارجی انجام می‌دهیم که نتیجه‌اش را پس از ۲ ثانیه روی کانال c1 برمی‌گرداند. توجه کنید که کانال بافردار است، پس ارسال در گوروتین مسدود نمی‌شود. این الگویی رایج برای جلوگیری از نشت گوروتین (goroutine leak) است، در صورتی که هیچ‌وقت از کانال خوانده نشود.

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

این select مهلت زمانی را پیاده‌سازی می‌کند. res := <-c1 منتظر نتیجه می‌ماند و <-time.After منتظر مقداری می‌ماند که پس از مهلت ۱ ثانیه فرستاده می‌شود. چون select با اولین دریافتی که آماده شود جلو می‌رود، اگر عملیات بیشتر از ۱ ثانیهٔ مجاز طول بکشد، حالت timeout انتخاب می‌شود.

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

اگر مهلت طولانی‌تری مثل ۳ ثانیه بدهیم، دریافت از 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")
    }
}

اجرای این برنامه نشان می‌دهد که عملیات اول به timeout می‌خورد و عملیات دوم موفق می‌شود.

$ go run timeouts.go 
timeout 1
result 2

مثال بعدی: .