go语言中context的使用说明
概述
Context
是 Go 语言中非常重要的一个概念,它主要用于跨多个函数或 goroutine 传递 取消信号、超时控制、截止时间 和 请求范围数据。
在并发编程中,Context
提供了更好的控制和管理,尤其是当你需要在多个 goroutine 之间传递状态或进行资源清理时。
主要功能
Context
主要有以下几个功能:
- 取消信号:通知一个或多个 goroutine 取消它们正在执行的工作。
- 超时和截止时间:指定操作的最大执行时间,防止阻塞操作过长时间。
- 传递请求范围数据:携带请求范围内的数据,通常用于请求 ID、用户信息等。
Context 的三种基本类型
Go 中的 context
包提供了几种常用的 Context
类型:
context.Background()
:通常作为根Context
,表示没有附加数据或取消信号的上下文。它通常是根上下文,作为其他上下文的父上下文。context.TODO()
:表示你暂时没有确定使用什么样的Context
,通常用于占位。context.WithCancel(parent)
:创建一个可取消的Context
,并返回一个取消函数,当你调用这个函数时,Context
会被取消。context.WithTimeout(parent, timeout)
:创建一个带有超时的Context
,指定最大等待时间,超过这个时间会自动取消。context.WithDeadline(parent, deadline)
:指定一个具体的截止时间,超过这个时间后自动取消。context.WithValue(parent, key, value)
:创建一个携带键值对数据的Context
,通常用于传递请求级别的数据(例如,用户身份信息)。
常见用法举例
context.WithCancel传递取消信号
主要场景:
- 手动控制并发任务的终止。
- 优雅退出:在一个任务中途需要取消时,用
cancel()
通知所有相关的 goroutine 停止执行。
代码示例:
package main import ( "context" "fmt" "time" ) func main() { // 创建一个可取消的 Context ctx, cancel := context.WithCancel(context.Background()) // 启动一个 goroutine,监听取消信号 go func(ctx context.Context) { for { select { case <-ctx.Done(): // 检测到取消信号 fmt.Println("Goroutine stopped") return default: // 模拟工作 fmt.Println("Working...") time.Sleep(1 * time.Second) } } }(ctx) // 主线程等待 3 秒后取消 time.Sleep(3 * time.Second) cancel() // 发送取消信号 // 等待 goroutine 退出 time.Sleep(1 * time.Second) fmt.Println("Main program exited") }
解释:
- 主线程创建了一个带有取消功能的上下文
ctx
。 - 子 goroutine 使用
ctx.Done()
监听取消信号。 - 主线程 3 秒后调用
cancel()
,子 goroutine 检测到信号后优雅退出。
使用 WithTimeout 设置超时
context.WithTimeout
用于设置一个超时时间,超过该时间后 Context
会自动取消,适用于需要限时执行的操作。防止某些任务阻塞的时间过长。
package main import ( "context" "fmt" "time" ) func main() { // 设置超时时间为 2 秒 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() // 确保超时后取消 ctx // 启动一个模拟长时间执行的任务 go longRunningTask(ctx) // 等待超时或任务完成 <-ctx.Done() if ctx.Err() == context.DeadlineExceeded { fmt.Println("Timeout reached") } } func longRunningTask(ctx context.Context) { select { case <-time.After(3 * time.Second): // 模拟长时间任务 fmt.Println("Task completed") case <-ctx.Done(): // 任务被取消或超时 fmt.Println("Task cancelled due to timeout") } }
WithDeadline
的用法和Withtimeout
用法类似,只是一个传入的参数是等待时间,一个传入的参数是截止时间。
使用 WithValue 传递数据
context.WithValue
可以在 Context
中存储键值对,通常用于传递请求级别的数据(例如用户身份、请求 ID 等)。
package main import ( "context" "fmt" ) func main() { // 创建一个上下文并传递数据 ctx := context.WithValue(context.Background(), "userID", 12345) // 将 ctx 传递给其他函数 processRequest(ctx) } func processRequest(ctx context.Context) { // 从 ctx 中提取数据 userID := ctx.Value("userID") if userID != nil { fmt.Println("User ID:", userID) } else { fmt.Println("No user ID found") } }
使用 WithValue
小心:context.WithValue
并不是用于传递大量数据的,主要用于传递少量的上下文信息,比如请求 ID 等。
如果传递过多的数据,会使得 Context
难以维护。
常用的相关方法和常量
ctx.Done()
:返回一个 channel,当Context
被取消时该 channel 会被关闭。ctx.Err()
:返回Context
被取消的错误,通常是context.Canceled
或context.DeadlineExceeded
。ctx.Value(key)
:获取在Context
中传递的数据。
context如何控制goroutine的执行
从上面的举例可以看出,在每个goroutine中通过判断ctx.Done()是否被执行,从而知道任务是否被取消/超时/到达截止日期。
当 Context
被取消(调用 cancel()
)或超时/到达截止时间时,ctx.Done()
所关联的 channel 会关闭,此时select语句就可以执行ctx.Done()
对应的分支。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
Golang中函数(Function)和方法(Method)的区别详解
在Golang中,大家必然会频繁使用到函数(Function)和方法(Method),但是有的同学可能并没有注意过函数和方法的异同点,函数和方法都是用来执行特定任务的代码块,虽然很相似,但也有很大的区别,所以本文将详细讲解函数和方法的定义以及它们的异同点2023-07-07
最新评论