テック技報最新号へ →
コード

Go: context.AfterFunc でキャンセル時の後始末を登録する(見張り用 goroutine が要らない)

Go 1.21 で入った context.AfterFunc(ctx, f) は、ctx が終わった(キャンセルまたは期限切れ)ときに一度だけ f を別の goroutine で実行する。戻り値の stop で登録を取り消せる。

@amitsinha__07 · 2026-11-05
#go#context#concurrency#net

使う場面

conn.Read のように context を受け取らないブロッキング処理を、キャンセルに合わせて止めたいとき。これまでは go func() { <-ctx.Done(); conn.Close() }() のような見張り役を自分で立て、処理が正常に終わったときにその goroutine を確実に終わらせる工夫も必要だった。AfterFunc ならその両方を標準ライブラリに任せられる。

やり方

go
func readWithContext(ctx context.Context, conn net.Conn, buf []byte) (int, error) {
    // ctx が終わったら接続を閉じて、ブロック中の Read を戻す
    stop := context.AfterFunc(ctx, func() {
        conn.Close()
    })
    defer stop() // 正常に読み終えたら登録を外す(f は呼ばれない)

    n, err := conn.Read(buf)
    if ctx.Err() != nil {
        return n, ctx.Err() // 閉じられたのはキャンセルのせい
    }
    return n, err
}
  • 接続を閉じたくない場合は、f の中で conn.SetReadDeadline(time.Now()) を呼んで Read だけ戻す書き方もある。
  • stop() は「f の実行を止められたか」を bool で返す。false なら f はすでに走り始めている(または ctx がもう終わっていた)。

注意点

出典

X の元の投稿を開く