美文网首页
golang context 看这篇就够啦

golang context 看这篇就够啦

作者: 护念 | 来源:发表于2023-11-17 17:02 被阅读0次

context

context字面意思是上下文,它有什么用呢?
它主要用于多gorountine、多层级的goroutine(一个goroutine下又有goroutine-子goroutine)的情况下,上层对于开启的gorotine的取消。

另外一个作用是,传递值,可以将一些特殊值塞入context中,在后续可以取出来。

ok,我们简单总结下context的作用:

  1. 控制goroutine的取消
  2. 值传递

下面我们从代码层面说明,先看最简单的值传递。

1.值传递

1.1 context.WithValue值传递

这个最简单,需要注意的是

  1. 这里的key不能用golang的内置类型,需要自定义一个类型;
  2. Value() 取出之前的存的值
package main

import (
    "context"
    "fmt"
)

// 上下文值类型 需要自定义类型
type mystring string

func main() {
    // mystring("abc") 将“bac"转换成mystring类型
    ctx := context.WithValue(context.Background(), mystring("abc"), "hello")
    doTask(ctx)
}

func doTask(ctx context.Context) {
    // 从上下文中取出值
    fmt.Println(ctx.Value(mystring("abc")))
}

// hello

2. 取消

2.1 取消概述

下面我们来看下取消,取消主要有两种:

  1. 主动调用cancel
  2. 到期取消timeout / deadline

如何实现goroutine的取消呢?
是通过Done()方法实现,它是通道;在超时或者手动取消后,会有值弹出;
在后续goroutine中通过监测Done()通道实现取消。

PS:

  1. 在context建立后,需要cancel保证资源的释放
  2. 在取消后,多次调用Done()都会有值返回

好啦!下面看代码

2.2 手动cancel

package main

import (
    "context"
    "log"
    "time"
)

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel() // 保证资源释放

    go doTask(ctx)              // goroutine中做任务
    time.Sleep(5 * time.Second) // 5s 后触发取消
    cancel()
    time.Sleep(time.Second) // 等待1s后退出 看到 ctx.Done内容
}

func doTask(ctx context.Context) {
    for {
        select {
        case <-ctx.Done(): // 收到取消
            log.Printf("取消啦:%s\n", ctx.Err())
            return // 退出for循环
        default:
        }

        time.Sleep(time.Second) //耗时间操作
        log.Printf("do task ...\n")
    }
}

// 2023/11/18 16:42:04 do task ...
// 2023/11/18 16:42:05 do task ...
// 2023/11/18 16:42:06 do task ...
// 2023/11/18 16:42:07 do task ...
// 2023/11/18 16:42:08 do task ...
// 2023/11/18 16:42:08 取消啦:context canceled

2.3 超时取消

通过context.WithTimeout或者context.Deadline实现
这两者使用非常相似,差别在于一个传递的是到期时间,另外一个传时间间隔。

// 传时间间隔
ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
// 传时间
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))

完整代码如下:

package main

import (
    "context"
    "log"
    "sync"
    "time"
)

var w sync.WaitGroup

func main() {
    // 5s超时间
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    w.Add(1)
    go doTask(ctx) // goroutine中做任务

    w.Wait() // 等待执行完
}

func doTask(ctx context.Context) {
    for {
        select {
        case <-ctx.Done(): // 操时收到
            log.Printf("取消啦:%s\n", ctx.Err())
            w.Done()
            return // 退出for循环
        default:
        }

        time.Sleep(time.Second) //耗时间操作
        log.Printf("do task ...\n")
    }
}

// 2023/11/18 16:51:40 do task ...
// 2023/11/18 16:51:41 do task ...
// 2023/11/18 16:51:42 do task ...
// 2023/11/18 16:51:43 do task ...
// 2023/11/18 16:51:44 do task ...
// 2023/11/18 16:51:44 取消啦:context deadline exceeded

2.4 多层取消

上面我们演示的都是一层的gorotine的取消,下面看看多层取消(goroutine 又生成goroutine的情况)

package main

import (
    "context"
    "log"
    "time"
)

func main() {
    // 5s超时间
    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*5))
    defer cancel()

    go doTask(ctx) // goroutine中做任务

    time.Sleep(6 * time.Second) // 留一定时间看task输出
}

func doTask(ctx context.Context) {
    // 子goroutine
    go doChildTask(ctx)

    for {
        select {
        case <-ctx.Done(): // 操时收到
            log.Printf("do task取消啦: %s\n", ctx.Err())
            return // 退出for循环
        default:
        }

        time.Sleep(time.Second) //耗时间操作
        log.Printf("do task ...\n")
    }
}

// task的子task
func doChildTask(ctx context.Context) {
    for {
        select {
        case <-ctx.Done(): // 操时收到
            log.Printf("child task取消啦: %s\n", ctx.Err())
            return // 退出for循环
        default:
        }

        time.Sleep(time.Second) //耗时间操作
        log.Printf("do child task ...\n")
    }
}


// 2023/11/18 16:59:34 do child task ...
// 2023/11/18 16:59:34 do task ...
// 2023/11/18 16:59:35 do child task ...
// 2023/11/18 16:59:35 do task ...
// 2023/11/18 16:59:36 do task ...
// 2023/11/18 16:59:36 do child task ...
// 2023/11/18 16:59:37 do child task ...
// 2023/11/18 16:59:37 do task ...
// 2023/11/18 16:59:38 do child task ...
// 2023/11/18 16:59:38 do task ...
// 2023/11/18 16:59:38 child task取消啦: context deadline exceeded
// 2023/11/18 16:59:38 do task取消啦: context deadline exceeded

相关文章

  • Golang Context分析

    [TOC] Golang Context分析 Context背景 和 适用场景 golang在1.6.2的时候还没...

  • 备孕?看这篇就够啦!

    结婚是人生大事!无论你是准备结婚还是刚刚结婚,都会幻想过将来宝宝的名字吧,ta长什么样子?跟谁更像呢?身高随爸爸还...

  • 利用context保存数据

    context[https://golang.org/pkg/context/#example_WithValue...

  • Golang 之context库用法

    1. context Golang 中的context 是Go语言在 golang1.7 发布时新增的标准包目的...

  • Android地理位置这篇就够啦!

    Android 系统提供了地理位置服务相关的API,方便了开发者去获得当前地理位置。用户可以通过GPS接收器接受地...

  • 表情包素材看这篇就够啦

    你有没有这种感觉,自己也算个有趣的人, 但是,手头可怜的表情包,根本不足以支撑自己风骚的内心 每次看别人把表情包用...

  • mysql索引原理,看这篇就够啦

    前言 网上已经有了很多相关mysql索引原理的文章,但是都存在一些问题,有的是直接复制别人的比较老的文章,有的直接...

  • 苏州馄饨攻略 看这篇就够啦

    终于出炉的「苏州馄饨攻略」—— 从吃到写,历时几个月,终于做好啦! 一共20家,有传统菜肉的,也有网红咸蛋黄芝士的...

  • Golang Context 详细原理和使用技巧

    Golang Context 详细原理和使用技巧 Context 背景 和 适用场景 Context 的背景 Go...

  • Golang context初探

    什么是context 从go1.7开始,golang.org/x/net/context包正式作为context包...

网友评论

      本文标题:golang context 看这篇就够啦

      本文链接:https://www.haomeiwen.com/subject/upfuwdtx.html