您现在的位置是:首页 >技术教程 >GOland的context的使用网站首页技术教程

GOland的context的使用

卑微的小鬼 2025-02-15 12:01:04
简介GOland的context的使用

超时控制

在 HTTP 请求、数据库查询或 RPC 调用等操作中,防止请求长时间阻塞。

package main

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

func main() {
	// 设置 2 秒超时
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel() // 确保超时后释放资源

	result := make(chan string)
	go func() {
		time.Sleep(3 * time.Second) // 模拟耗时任务
		result <- "任务完成"
	}()

    select {
        case res := <-result:
        	fmt.Println(res)  // 如果 `result` 先返回数据,就打印结果
        case <-ctx.Done():
        	fmt.Println("任务超时,取消操作") // 如果 `ctx.Done()` 先触发,说明超时了
    }
}



context.WithTimeout() 允许在设定时间后自动取消操作,避免 goroutine 长时间阻塞

手动取消

如果一个主 goroutine 需要通知子 goroutine 停止执行,可以使用 context.WithCancel()

package main

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

func worker(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("收到取消信号,退出")
			return
		default:
			fmt.Println("工作中...")
			time.Sleep(500 * time.Millisecond)
		}
	}
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())

	go worker(ctx)

	time.Sleep(2 * time.Second)
	fmt.Println("取消任务")
	cancel() // 发送取消信号
	time.Sleep(1 * time.Second) // 等待 goroutine 退出
}


在 TCP 服务器中,我们可能需要在主程序退出时,通知所有客户端断开连接。

package main

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

func handleConnection(ctx context.Context, conn net.Conn) {
	defer conn.Close()
	for {
		select {
		case <-ctx.Done():
			fmt.Println("断开客户端连接:", conn.RemoteAddr())
			return
		default:
			time.Sleep(1 * time.Second) // 模拟处理
			fmt.Println("服务客户端:", conn.RemoteAddr())
		}
	}
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())

	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Println("监听失败:", err)
		return
	}
	defer ln.Close()

	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				fmt.Println("接受连接失败:", err)
				continue
			}
			go handleConnection(ctx, conn)
		}
	}()

	time.Sleep(10 * time.Second) // 服务器运行 10 秒
	fmt.Println("关闭服务器,断开所有连接")
	cancel() // 发送取消信号,所有连接都会断开

	time.Sleep(2 * time.Second) // 等待 goroutine 退出
	fmt.Println("服务器已关闭")
}

context.WithCancel() 适用于 任务需要手动终止 的场景,如定期任务、消息队列消费等。

传递请求作用域的数据

可以用 context.WithValue() 传递 请求相关的元数据,比如 用户身份信息、trace ID

package main

import (
	"context"
	"fmt"
)

// 定义一个键的类型,避免键冲突
type contextKey string

func processRequest(ctx context.Context) {
	uid := ctx.Value(contextKey("userID"))
	fmt.Println("处理请求的用户 ID:", uid)
}

func main() {
	ctx := context.WithValue(context.Background(), contextKey("userID"), 12345)
	processRequest(ctx)
}

context.WithValue() 适用于 日志追踪、用户身份验证 等场景,但不建议传递大数据结构(会影响性能)。

与 HTTP 服务器结合

在 Web 服务器中,Go 的 http.Request 自带 Context() 方法,可以获取请求的 context,用于控制请求生命周期

package main

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

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	fmt.Println("处理请求")

	select {
	case <-time.After(3 * time.Second):
		fmt.Fprintln(w, "请求处理完成")
	case <-ctx.Done():
		fmt.Fprintln(w, "请求取消")
	}
}

func main() {
	http.HandleFunc("/", handler)
	server := &http.Server{
		Addr: ":8080",
	}

	go func() {
		time.Sleep(2 * time.Second)
		server.Shutdown(context.Background()) // 2 秒后关闭服务器
	}()

	fmt.Println("服务器启动在 8080 端口")
	if err := server.ListenAndServe(); err != nil {
		fmt.Println("服务器已关闭")
	}
}

当客户端断开连接,ctx.Done() 会触发,避免继续执行无用的操作。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。