Programarea Asincronă a Curățării la Expirarea Contextului cu context.AfterFunc în Go

Ghid de utilizare a funcției context.AfterFunc introduse în Go 1.21: executarea automată și asincronă a funcțiilor de eliberare a resurselor sau întrerupere a operațiunilor de I/O imediat ce un context este anulat.

Sintaxă
stop := context.AfterFunc(ctx, func() { conn.Close() }); defer stop()

Înainte de Go 1.21, pentru a executa o acțiune atunci când un context era anulat (de exemplu, forțarea închiderii unei conexiuni de rețea sau întreruperea unui proces extern), programatorii erau forțați să pornească o goroutine dedicată care asculta pe canalul <-ctx.Done(). Această practică irosea resurse și introducea riscuri de scurgeri de goroutine-uri. Funcția context.AfterFunc din Go 1.21 programează o funcție de callback care va fi executată automat într-o goroutine nouă imediat ce contextul este anulat sau expiră.

Întreruperea unei conexiuni lente la timeout cu context.AfterFunc
package main

import (
    "context"
    "fmt"
    "net"
    "time"
)

func descarcaDateSecurizat(ctx context.Context, adresa string) error {
    conn, err := net.Dial("tcp", adresa)
    if err != nil {
        return err
    }
    defer conn.Close()

    // Programăm închiderea forțată a socket-ului dacă contextul este anulat:
    stopCuratare := context.AfterFunc(ctx, func() {
        fmt.Println("Context anulat! Se forțează închiderea conexiunii TCP...")
        conn.Close()
    })
    // Dacă funcția se termină cu succes înainte de anulare, anulăm callback-ul:
    defer stopCuratare()

    // Operațiune de citire de lungă durată:
    buffer := make([]byte, 1024)
    _, err = conn.Read(buffer)
    return err
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
    defer cancel()

    _ = descarcaDateSecurizat(ctx, "example.com:80")
}