一、创建context的两种方式
func Background
func Background() Context
Background returns a non-nil, empty Context. It is never canceled, has no values, and has no deadline. It is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests.
background获得一个根context,这个context不能被取消 cancel(),用在main函数或则顶级请求中用来派生其他context
func TODO
func TODO() Context
TODO returns a non-nil, empty Context. Code should use context.TODO when it's unclear which Context to use or it is not yet available (because the surrounding function has not yet been extended to accept a Context parameter). TODO is recognized by static analysis tools that determine whether Contexts are propagated correctly in a program.
获得一个空context ,用于不确定context类型时
二、派生context
用空context派生出新context,方法有三:
-
ctx3, cancel := context.WithCancel(ctx)
返回context和一个cancel函数,后面自己手动调用cancel函数发出取消信号
-
ctx4, cancel := context.WithTimeout(ctx, 2*time.Second)
在指定时间后自动cancel
-
ctx5, cancel := context.WithDeadline(ctx, time.Time{})
在到达指定时间后自动cancel
三、context的使用
规范、推荐:将context作为第一个参数传入函数,函数监听context是否被cancel
context.Donw()返回一个只读的channel,当context被取消时,这个通道里会传入一个值表示他已经被关闭,因此函数应该用select监听这个通道是否有值传入
让main这个客户只等待2秒,超过就发送信号结束工作协程,避免浪费资源
func main() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
go func() {
time.Sleep(2 * time.Second)
cancel()
}()
Dosomething(ctx)
}
//会在5秒之后执行完毕并返回
func Dosomething(ctx context.Context) {
select {
case <-ctx.Done():
err := ctx.Err()
if err != nil {
fmt.Println(err.Error())
}
case <-time.After(5 * time.Second):
fmt.Println("task finished") //模拟任务花费5秒完成
}
}
三、多服务端与客户端的情况,多进程
上面是单进程,下面演示多进程
客户端,client.go
func HandleFunc(w http.ResponseWriter, r *http.Request) {
fmt.Println("handling request")
ctx := r.Context()
//模拟任务执行
compete := make(chan struct{})
go func() {
time.Sleep(5 * time.Second)
compete <- struct{}{}
}()
select {
case <-compete:
fmt.Println("task finished")
case <-ctx.Done():
err := ctx.Err()
if err != nil {
fmt.Println(err)
}
}
}
func main() {
http.HandleFunc("/", HandleFunc)
log.Fatalln(http.ListenAndServe(":8080", nil))
}
服务端 serve.go
func main() {
//创建上下文
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
//传入上下文,2种在http加入context的方法
//req, err := http.NewRequest(http.MethodGet, "/", nil)
//if err != nil {
// log.Fatal(err)
//}
//req = req.WithContext(ctx)
resp, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8080", nil)
if err != nil {
log.Fatal(err)
}
ret, err := http.DefaultClient.Do(resp)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
r, err := ioutil.ReadAll(ret.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf(string(r))
}