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

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

时间2025-06-14 15:26:37分类IT科技浏览4545
导读:今天这篇笔记我们来学习一下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
python多线程没用(python threading实现线程的过程) vue @事件(Vue事件修饰符使用详细介绍)