go并发编程之一(上下文context) - Go语言中文社区

go并发编程之一(上下文context)


go语言中关于上下文相关的知识

1. 上下文的组成

在这里插入图片描述

1.1 树根

上下文在某个环境中定义了两棵树,分别是:

A

ctxA := context.Background()

B

ctxB := context.TODO()

可以把AB作为树的根节点,从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.WithDeadlinecontext.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

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/wujiangwei567/article/details/86680216
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-02-25 01:17:11
  • 阅读 ( 2780 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢