Golang Context 详解

当需要在多个 goroutine 中传递上下文信息时,可以使用 Context 实现。Context 除了用来传递上下文信息,还可以用于传递终结执行子任务的相关信号,中止多个执行子任务的 goroutine。Context 中提供以下接口:

type Context interface {

    Deadline() (deadline time.Time, ok bool)

    Done() <-chan struct{}

    Err() error

    Value(key interface{}) interface{}

}
  • Deadline 方法,返回 Context 被取消的时间,也就是完成工作的截止日期;

  • Done,返回一个 channel,这个 channel 会在当前工作完成或者上下文被取消之后关闭,多次调用 Done 方法会返回同一个 channel;

  • Err 方法,返回 Context 结束的原因,它只会在 Done 返回的 channel 被关闭时才会返回非空的值,如果 Context 被取消,会返回 Canceled 错误;如果 Context 超时,会返回 DeadlineExceeded 错误。

  • Value 方法,可用于从 Context 中获取传递的键值信息。

在 Web 请求的处理过程中,一个请求可能启动多个 goroutine 协同工作,这些 goroutine 之间可能需要共享请求的信息,且当请求被取消或者执行超时时,该请求对应的所有 goroutine 都需要快速结束,释放资源。Context 就是为了解决上述场景而开发的,我们通过下面一个例子来演示:

package main

import (

	"context"

	"fmt"

	"time"

)

const DB_ADDRESS  = "db_address"

const CALCULATE_VALUE  = "calculate_value"

func readDB(ctx context.Context, cost time.Duration)  {

	fmt.Println("db address is", ctx.Value(DB_ADDRESS))

	select {

	case <- time.After(cost): //  模拟数据库读取

		fmt.Println("read data from db")

	case <-ctx.Done():

		fmt.Println(ctx.Err()) // 任务取消的原因

		// 一些清理工作

	}

}

func calculate(ctx context.Context, cost time.Duration)  {

	fmt.Println("calculate value is", ctx.Value(CALCULATE_VALUE))

	select {

	case <- time.After(cost): //  模拟数据计算

		fmt.Println("calculate finish")

	case <-ctx.Done():

		fmt.Println(ctx.Err()) // 任务取消的原因

		// 一些清理工作

	}

}

func main()  {

	ctx := context.Background(); // 创建一个空的上下文

	// 添加上下文信息

	ctx = context.WithValue(ctx, DB_ADDRESS, "localhost:10086")

	ctx = context.WithValue(ctx, CALCULATE_VALUE, 1234)

	// 设定子 Context 2s 后执行超时返回

	ctx, cancel := context.WithTimeout(ctx, time.Second * 2)

	defer cancel()

	// 设定执行时间为 4 s

	go readDB(ctx, time.Second * 4)

	go calculate(ctx, time.Second * 4)

	// 充分执行

    time.Sleep(time.Second * 5)

}

在上述例子中,我们模拟了一个请求中同时进行数据库访问和逻辑计算的操作,在请求执行超时时,及时关闭尚未执行结束 goroutine。我们首先通过 context.WithValue 方法为 context 添加上下文信息,Context 在多个 goroutine 中是并发安全的,可以安全地在多个 goroutine 中对 Context 中的上下文数据进行读取。接着使用 context.WithTimeout 方法设定了 Context 的超时时间为 2s,并传递给 readDB 和 calculate 两个 goroutine 执行子任务。在 readDB 和 calculate 方法中,使用 select 语句对 Context 的 Done 通道进行监控。由于我们设定了子 Context 将在 2s 之后超时,所以它将在 2s 之后关闭 Done 通道;然而预设的子任务执行时间为 4s,对应的 case 语句尚未返回,执行被取消,进入到清理工作的 case 语句中,结束掉当前的 goroutine 所执行的任务。

使用 Context,能够有效地在一组 goroutine 中传递共享值、取消信号、deadline 等信息,及时关闭不需要的 goroutine。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿磊的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值