Go标准库Context

在 Go 语言中,context 包提供了一种在函数之间传递请求作用域的方法。它通常用于跨 API 边界传递取消信号、超时值、截止时间以及请求范围的数据。

一、Context 接口

context.Context 接口是 Go 语言中用于管理请求作用域的重要接口之一。它定义了一组方法,用于传递取消信号、超时值、截止时间以及请求范围的数据。

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline() (deadline time.Time, ok bool)
    返回上下文的截止时间(deadline),如果没有设置截止时间,则 ok 为 false。可以用于检查上下文是否有截止时间,并获取该截止时间。

  • Done() <-chan struct{}
    返回一个通道Channel(<-chan struct{}),当上下文被取消或超时时,该通道会被关闭。可以通过监听这个通道来接收取消信号,执行相应的清理或中止操作。

  • Err() error
    返回上下文取消的原因,如果上下文没有被取消,则返回 nil。可以用于检查上下文是否已被取消,并获取取消的具体原因。

  • Value(key interface{}) interface{}
    返回与键关联的值,如果键不存在则返回nil。可以用于在请求范围内传递键值对,比如传递请求标识、用户认证信息等。

context包主要提供了两种方式创建context:

  • context.Backgroud():是上下文的默认值,所有其他的上下文都应该从它衍生(Derived)出来。
  • context.TODO():只在不确定应该使用哪种上下文时使用。

这两个函数其实只是互为别名,没有差别。

二、Contex的With系列函数

2.1 WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

context.WithCancel函数用于创建一个带有取消功能的上下文(Context)。当调用返回的CancelFunc时,所有通过该上下文启动的goroutine都会收到一个取消信号。这通常用于需要响应外部请求取消的场景,比如HTTP长轮询或者WebSocket连接。

实际使用context.WithCancel时,你需要确保正确地处理了取消信号,并且在适当的时候调用了cancel函数,以避免资源泄漏或不必要的CPU消耗

package main

import (
	"context"
	"fmt"
	"time"
)

// 模拟一个长时间运行的任务
func longRunningTask(ctx context.Context, name string) {
	for i := 0; ; i++ {
		select {
		case <-ctx.Done(): // 检查上下文是否被取消
			return // 如果被取消,立即返回
		case <-time.After(1 * time.Second): // 每秒输出一次进度
			fmt.Printf("%s: %d
", name, i)
		}
	}
}

func main() {
	// 创建一个新的带取消功能的上下文
	ctx, cancel := context.WithCancel(context.Background())

	// 使用新创建的上下文启动任务
	go longRunningTask(ctx, "task")

	// 主线程等待5秒后取消上下文
	time.Sleep(5 * time.Second)
	cancel() // 发送取消信号

	// 为了演示,我们再等2秒钟以确保任务已经接收到了取消信号并且退出
	time.Sleep(2 * time.Second)
}

2.2 WithDeadline

func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)

WithDeadline函数用于创建一个带有截止时间的新的Context。这个截止时间可以用来限制操作的执行时间,当超过截止时间时,可以自动取消Context

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	parentCtx := context.Background()
	deadline := time.Now().Add(time.Millisecond * 50)
	// 创建一个带有截止时间的新context,设置截止时间为当前时间的50毫秒之后
	ctx, cancel := context.WithDeadline(parentCtx, deadline)
	// 确保在main函数返回前取消context以释放资源
	defer cancel()

	// 模拟一个需要执行的操作,比如等待100毫秒
	go func() {
		select {
		case <-time.After(100 * time.Millisecond):
			fmt.Println("操作完成")
		case <-ctx.Done():
			fmt.Println("操作超时")
		}
	}()

	// 等待一段时间,以便观察操作是否在截止时间之前完成
	time.Sleep(time.Millisecond * 200)
}

我们设置截止时间为当前时间的50毫秒之后。接着,我们启动一个goroutine来模拟一个长时间运行的操作,通过监听ctx.Done()来判断操作是否在截止时间之前完成。在主函数结束前,我们通过defer语句调用cancel函数来确保及时释放资源。

2.3 WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout是一种用于设置操作执行的超时时间的机制,它允许你指定一个时间段,在这个时间段内如果操作没有完成,就会抛出一个超时异常或执行一个指定的回调函数。

package main

import (
	"context"
	"fmt"
	"time"
)


func myFunction(ctx context.Context, duration time.Duration) {
	select {
	case <-time.After(duration):
		fmt.Println("操作成功")
	case <- ctx.Done():
		fmt.Println("操作失败")
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	myFunction(ctx, 2*time.Second)
}

2.4 WithValue

func WithValue(parent Context, key, val interface{}) Context

WithValue用于创建一个带有额外值的新上下文,这个值可以在上下文中进行传递。通常情况下,WithValue方法用于将一些请求范围的值(例如请求ID、用户信息等)与上下文关联起来,以便在整个请求处理过程中进行传递和访问。

需要注意的是,在使用context.WithValue方法时,应避免直接使用内置类型string作为键,因为这可能导致潜在的键冲突问题。考虑到context.WithValue方法接受interface{}类型的键和值,如果在不同的地方使用了相同的字符串作为键,那么就会导致冲突。为了避免这种情况,建议定义自己的类型作为键

package main

import (
	"context"
	"fmt"
)

// 定义一个自定义类型作为键
type contextKey string

func processRequest(ctx context.Context) {
	// 从上下文中获取请求ID
	reqID := ctx.Value(contextKey("requestID")).(int)

	// 模拟处理请求的操作
	fmt.Printf("Processing request with ID %d\n", reqID)
}

func main() {
	// 创建一个带有请求ID的上下文
	ctx := context.WithValue(context.Background(), contextKey("requestID"), 12345)

	// 处理请求
	processRequest(ctx)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

闲谈社

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

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

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

打赏作者

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

抵扣说明:

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

余额充值