go并发编程之一(上下文context)
go语言中关于上下文相关的知识
文章目录
1. 上下文的组成
1.1 树根
上下文在某个环境中定义了两棵树,分别是:
树A
ctxA := context.Background()
树B
ctxB := context.TODO()
可以把A
和B
作为树的根节点,从context
包里能看到这两个方法的底层实现其实一模一样,可能
是为了某个环境需要有多个上下文同时使用而设立的
1.2 树叶
1.2.1 context.WithCancel
树叶包括四个方法:context.WithCancel
带取消方法的派生上下文
ctxC,cancel := context.WithCancel(ctxA)
defer cancel()
1.2.2 context.WithDeadline和context.WithTimeout
context.WithDeadline
和 context.WithTimeout
都是超时自动取消,其中 timeout的底层
实现还是deadline,只不过换了个形式罢了
ctxD,cancel := context.WithDeadline(ctx,time.Now().Add(1 * time.Second))
ctxE,cancel := context.WithTimeout(ctx,1 * time.Second)
1.2.1 context.WithValue
context.WithValue
给上下文添加键值对
添加:
const GOLABLE_KEY = "context"
ctxF = context.WithValue(ctxA, GOLABLE_KEY, "hello world")
取值:
value := ctxF.Value(GOLABLE_KEY).(string)
log.Println(value)
2. 上下文传递值
我们有时候会遇到某个上下文在传递的时候,需要进行数据的检查,这时候可以用上
context.WithValue
----存储和检索附加于请求的数据包
如下例:
package main
import (
"context"
"fmt"
)
func main() {
//验证 请求数据包
ProcessRequest("jane", "abc123")
}
type ctxKey int
const (
ctxUserID ctxKey = iota
ctxAuthToken
)
func UserID(c context.Context) string {
return c.Value(ctxUserID).(string)
}
func AuthToken(c context.Context) string {
return c.Value(ctxAuthToken).(string)
}
func ProcessRequest(userID, authToken string) {
ctx := context.WithValue(context.Background(), ctxUserID, userID)
ctx = context.WithValue(ctx, ctxAuthToken, authToken)
HandleResponse(ctx)
}
func HandleResponse(ctx context.Context) {
//认证处理逻辑 避免显示的传递
fmt.Printf(
"handling response for %v (auth: %v)",
UserID(ctx),
AuthToken(ctx),
)
}
3. 超时取消
有时候,我们会遇到比较耗费时间的程序,比如发送邮件等,这里我们用
time.After(5 * time.Second)
来模拟耗时5秒的处理过程通道,然后使用上下文来取消整个超时操作,释放阻塞资源, 程序稍微有点长
很容易不耐烦,所以加了张图 帮助理解
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
context.WithCancel(context.TODO())
defer cancel()
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
if err := printGreeting(ctx); err != nil {
fmt.Printf("%v", err)
return
}
}()
go func() {
defer wg.Done()
if err := printFarewell(ctx); err != nil {
fmt.Printf("%v", err)
return
}
}()
wg.Wait()
}
func printGreeting(ctx context.Context) error {
greeting, err := genGreeting(ctx)
if err != nil {
return err
}
fmt.Printf("%s world!\n", greeting)
return nil
}
func printFarewell(ctx context.Context) error {
farewell, err := genFarewell(ctx)
if err != nil {
return err
}
fmt.Printf("%s world!\n", farewell)
return nil
}
func genGreeting(ctx context.Context) (string, error) {
ctx1,cancle := context.WithDeadline(ctx,time.Now().Add(1 * time.Second))
defer cancle()
switch locale, err := locale(ctx1,"1"); {
case err != nil:
return "", err
case locale == "EN/US":
return "hello", nil
}
return "", fmt.Errorf("unsupported locale")
}
func genFarewell(ctx context.Context) (string, error) {
ctx1,cancle := context.WithTimeout(ctx,1 * time.Second)
defer cancle()
switch locale, err := locale(ctx1,"2"); {
case err != nil:
return "", err
case locale == "EN/US":
return "goodbye", nil
}
return "", fmt.Errorf("unsupported locale")
}
func locale(ctx context.Context,num string) (string, error) {
select {
case <-ctx.Done():
return "", fmt.Errorf("canceled" + num+"\n")
case <-time.After(5 * time.Second):
}
return "EN/US", nil
}
打印结果:
canceled2
canceled1
参考来源: https://exotel.com/blog/engineering/understanding-go-context-library/
https://code.tutsplus.com/tutorials/context-based-programming-in-go--cms-29290
https://www.kancloud.cn/mutouzhang/go/596849