【go从零单排】上下文(context)
🌈Don’t worry , just coding!
内耗与overthinking只会削弱你的精力,虚度你的光阴,每天迈出一小步,回头时发现已经走了很远。
📗概念
在 Go 语言中,上下文(context)是一个重要的概念,主要用于管理请求的生命周期、传递取消信号、超时控制以及传递请求范围内的值。上下文通常与并发编程相关,尤其是在处理 HTTP 请求和其他 I/O 操作时。
💻代码
http上下文
package main
import (
//fmt:用于格式化输入输出。
//net/http:提供 HTTP 客户端和服务器的功能。
//time:用于处理时间相关的功能。
"fmt"
"net/http"
"time"
)
// 定义了一个名为 hello 的处理函数,接受两个参数:
// w http.ResponseWriter:用于构建 HTTP 响应。
// req *http.Request:包含了 HTTP 请求的信息。
func hello(w http.ResponseWriter, req *http.Request) {
//从请求中获取上下文(Context),用于管理请求的生命周期和处理超时、取消等操作。
ctx := req.Context()
//打印日志,表示处理程序开始执行。
fmt.Println("server: hello handler started")
//defer 语句确保在函数结束时打印 "server: hello handler ended",即使在函数中发生了错误或提前返回。
defer fmt.Println("server: hello handler ended")
//使用 select 语句来处理两个可能的情况:
select {
//第一个 case 使用 time.After 创建一个 10 秒的延迟。如果在 10 秒内没有其他事件发生,服务器将向响应写入 "hello"。
case <-time.After(10 * time.Second):
fmt.Fprintf(w, "hello\n")
case <-ctx.Done(): //第二个 case 监听上下文的取消信号。如果请求被取消(例如,客户端关闭了连接),则执行该分支。
//获取上下文的错误信息(如请求被取消或超时),并打印出来。
err := ctx.Err()
fmt.Println("server:", err)
//使用 http.Error 向客户端返回一个 HTTP 错误响应,状态码为 500(内部服务器错误),并包含上下文的错误信息。
internalError := http.StatusInternalServerError
http.Error(w, err.Error(), internalError)
}
}
func main() {
//在 main 函数中,使用 http.HandleFunc 注册 /hello 路由,并将其与 hello 处理函数关联。
http.HandleFunc("/hello", hello)
//使用 http.ListenAndServe 启动 HTTP 服务器,监听在 8090 端口。
http.ListenAndServe(":8090", nil)
}
创建和使用上下文
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(2 * time.Second)
cancel() // 取消上下文
}()
select {
case <-time.After(5 * time.Second):
fmt.Println("Completed without cancellation")
case <-ctx.Done():
fmt.Println("Cancelled:", ctx.Err())
}
}
- 在这个例子中,创建了一个可取消的上下文 ctx。在一个 Goroutine 中,等待 2 秒后调用 cancel(),这将取消上下文。
- 主 Goroutine 使用 select 等待 5 秒或上下文的取消信号。
超时控制
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // 确保在函数结束时调用 cancel
select {
case <-time.After(3 * time.Second):
fmt.Println("Completed")
case <-ctx.Done():
fmt.Println("Cancelled:", ctx.Err())
}
}
- 在这个例子中,创建了一个带有 2 秒超时的上下文。如果在 2 秒内没有完成操作,ctx.Done() 将被触发,主 Goroutine 将打印取消信息。
传递值
package main
import (
"context"
"fmt"
)
func main() {
ctx := context.WithValue(context.Background(), "key", "value")
value := ctx.Value("key")
fmt.Println("Value in context:", value)
}
- 这里创建了一个上下文,并在其中存储了一个键值对。可以通过 ctx.Value(“key”) 获取存储的值。
- 上下文还可以用于在请求的处理链中传递值,但应谨慎使用,因为上下文的值是不可变的。
💡Tips
Go 的 context 包提供了用于创建和处理上下文的功能。常用的上下文类型有:
- context.Background():返回一个空的上下文,通常作为根上下文。
- context.TODO():返回一个空的上下文,表示尚未确定的上下文,适用于开发中的占位符。
- context.WithCancel(parent):创建一个可取消的上下文。
- context.WithTimeout(parent,
timeout):创建一个带有超时的上下文。 - context.WithDeadline(parent,
deadline):创建一个带有截止日期的上下文。 - context.WithValue(parent, key,
value):创建一个可以传递值的上下文。
💪无人扶我青云志,我自踏雪至山巅。