首页IT科技golang并发编程实战(《Go 语言并发之道》读书笔记(六))

golang并发编程实战(《Go 语言并发之道》读书笔记(六))

时间2025-05-02 12:51:19分类IT科技浏览3535
导读:今天这篇笔记我们来学习一下context包...

今天这篇笔记我们来学习一下context包

context包的一个应用场景是可以通过它控制goroutine的取消          ,超时等          。

我们先来看一个取消的例子

context.WithCancel

func doSomething(ctx context.Context) { ctx, cancelctx := context.WithCancel(ctx) printCh := make(chan int) go doAnother(ctx, printCh) for i := 0; i < 3; i++ { printCh <- i } cancelctx() fmt.Println("do something finished") } func doAnother(ctx context.Context, printCh chan int) { for { select { case <-ctx.Done(): if err := ctx.Err(); err != nil { fmt.Printf("do Another with error, %s \n", err) } fmt.Println("do Another finished") return case data := <-printCh: fmt.Printf("receive %d \n", data) } } }

我们在doSomething方法中加了一个可以取消的Context                  , 然后定义了一个channel, 往channel里面放入3个数      ,另外启动一个goroutine doAnother 来接收它, 在完成放入数据后我们调用了取消方法       , 通知doAnother 结束掉                  。 这样程序就达到一个goroutine通知另外一个goroutine的效果                 , 程序运行结果如下

receive 0 receive 1 receive 2 do Another with error, context canceled do Another finished do something finished

通过打印的输出我们可以看到doAnother是通过canneled来结束的      。

如果我们再加一个goroutine         ,也能够通过context来取消       。 这里比较简单不贴代码了                 。

context.WithTimeout

如果只是单纯的通知另外一个goroutine     ,直接通过close channel也可以做到了                , Context还可以通过WithTimeout设定超时时间来限定程序运行多久            ,我们简单改造一下上面的代码

func doSomething(ctx context.Context) { ctx, cancelctx := context.WithTimeout(ctx, 1500*time.Millisecond) defer cancelctx() printCh := make(chan int) go doAnother(ctx, printCh) outer: for i := 0; i < 3; i++ { select { case printCh <- i: time.Sleep(1 * time.Second) case <-ctx.Done(): break outer } } cancelctx() fmt.Println("do something finished")

我们把doSomething改造了一下   , 通过context.WithTimeout(ctx, 1500*time.Millisecond)将context 改成运行1.5秒就退出, doAnother 保持不变                , 然后发送的时候               ,发送一个就sleep 1秒         。 运行效果如下

receive 0 receive 1 do Another with error, context deadline exceeded do Another finished do something finished

我们可以看到只发送了两个数,就退出了             ,退出的原因是exceeded. 这样就达到了控制goroutine运行时间的效果     。

通过context.WithDeadline 也可以达到和WithTimeout一样的效果                  ,只是一个是按时间点来停   ,一个是按时间段来停          , 示例代码如下 deadLineTime := time.Now().Add(1500 * time.Millisecond) ctx, cancelctx := context.WithDeadline(ctx, deadLineTime)

context.WithValue

context还有个用途是用来记录和传递value, 比如我们可以向别的goroutine传递UserID                  ,RequestID等要跟踪的变量信息                。 还是通过示例代码来说明

func main() { var ctx = context.Background() key := 1 ctx = context.WithValue(ctx, key, "someValue") doSomething(ctx) } func doSomething(ctx context.Context) { fmt.Printf("Doing something! %s \n", ctx.Value(1)) }

输出结果很简单就是"Doing something! someValue" 这个someValue就是通过Context传递给doSomething方法里面的            。 如果ctx.Value的key不存在      ,也不会报错       ,会返回nil.

上面key的写法                 ,Visual Studio Code会提示            ”should not use built-in type int as key for value; define your own type to avoid collisions“

就是说这个key可能冲突         ,比如你的程序用String "userID", 别人也用String                 ”userID“, 你取的时候取到别人用的了     ,那样就给程序带来隐患                , 我们需要自定义key, 如下代码示例 type ctxKey int const ( ctxUserKey ctxKey = iota ctxAuthToken ) func main() { var ctx = context.Background() ctx = context.WithValue(ctx, ctxUserKey, "someValue") doSomething(ctx) } func doSomething(ctx context.Context) { fmt.Printf("Doing something! %s \n", ctx.Value(ctxUserKey)) }

我们定义了常量ctxUserKey            , 它是ctxKey类型   ,ctxUserKey它的实际值是0                , 在取值和赋值的时候我们同意用ctxUserKey               , 这样就能很清楚明白的拿到它的value. 如果我们用ctx.Value(0)是拿不到值的   。

最后

作者在书中说context包的包一直存在争议,为什么呢? 因为它可以放入任意类型             ,这样就让有些偷懒的程序员把它当垃圾桶                  , 程序运行需要的参数也用它来传递   ,这样它就被赋予了太多它不应该的功能                。 那么我们需要遵守一定的规则来使用它          ,作者给出了建议

1, 数据应该通过进程或API边界                  , 这句话感觉是翻译问题      ,不是特别清楚       ,我感觉作者的意思是通过明确的参数来传递数据                 ,而不是context

2         ,数据应该是不可变的               。 意思就是只WithValue一次     ,不要去修改它

3                ,数据应该趋向简单类型            , 保持简单很重要

4   ,数据应该是数据                ,而不是类型与方法               , 也是保持简单

5,数据应该用于装饰操作             ,而不是驱动操作。意思就是你不要通过context里面的内容来决定你的代码逻辑             。

作者最后说context提供的取消功能非常有用                  , 就是WithValue不要滥用                  。这一章感觉翻译的不好   ,读起来很晦涩          ,我还是看了另外一篇文章搞明白的   。 贴出链接在此https://www.digitalocean.com/community/tutorials/how-to-use-contexts-in-go

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
pythonmultiprocessing用法(python multiprocessing如何使用?) mac升级系统磁盘空间不足怎么解决(Mac系统不能升级Windows XP SP3提示磁盘空间不足)