您现在的位置是:首页 >技术教程 >GOland的context的使用网站首页技术教程
GOland的context的使用
简介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()
会触发,避免继续执行无用的操作。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。