首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
上下文
上下文和普通参数的区别
上下文树
上下文接口——Context
利用context.emptyCtx创建树的根节点
上下文树的构建
利用valueCtx实现信息透传
valueCtx用于参数传递
从父节点获得透传值
利用cancelCtx通知协程终止执行
通知子协程终止执行
通知子协程的实现过程
为什么需要取消函数
利用timerCtx实现定时取消
调用context.WithDeadline()创建定时器上下文
调用context.WithTimeout()创建定时器上下文
编程范例——上下文的典型应用场景
利用结构体传递参数
valueContext为什么需要key
利用cancelCtx同时取消多个子协程
反射
反射的意义
反射的API
利用reflect.TypeOf()来获得类型信息
利用reflect.Type.Kind()方法来获取类型的具体分类
利用reflect.Type.Element()方法来获取元素类型
类型断言的用法与局限性
值信息
利用reflect.ValueOf()来获得值信息
利用reflect.Value.Kind()来获得值的分类信息
利用reflect.Value.Elem()来获得值的元素信息
利用反射访问和修改值信息
利用反射机制动态调用方法
编程范例——动态方法调用
泛型
泛型的意义
泛型应用到函数
泛型函数的使用
泛型中的隐含信息
避免类型强制转换
泛型类型的单独定义
泛型导致接口定义的变化
接口定义的变化
空接口的二义性
接口类型的限制
泛型类型应用到receiver
泛型类型不能直接用于定义receiver
间接实现泛型定义receiver
编程范例——自定义队列的实现
当前位置:
首页>>
技术小册>>
深入浅出Go语言核心编程(五)
小册名称:深入浅出Go语言核心编程(五)
### 章节标题:利用`cancelCtx`通知协程终止执行 在Go语言的并发编程模型中,协程(goroutine)是轻量级的线程实现,它们允许程序以并发的方式执行多个任务。然而,随着协程数量的增加,如何有效管理这些协程的生命周期,确保它们能在适当的时候被创建、执行并终止,成为了一个重要的挑战。Go的`context`包为此提供了强大的支持,特别是通过`context.WithCancel`函数创建的`cancelCtx`,它允许我们优雅地通知并终止协程的执行。 #### 一、`context`包简介 在深入`cancelCtx`之前,有必要先了解`context`包的基本概念和用途。`context`包在Go 1.7中被引入,旨在解决跨API边界和进程间传递截止日期、取消信号以及其他请求范围的值的问题。`context`类型允许我们传递请求级别的值、取消信号和截止时间,而无需显式地将这些值作为参数传递给每一个函数。 `context.Context`是一个接口,它定义了四个方法: - `Deadline() (deadline time.Time, ok bool)`: 返回设置的截止时间(如果有的话),`ok`为`true`时表示设置了截止时间。 - `Done() <-chan struct{}`: 返回一个只读的channel,当上下文被取消或达到其截止时间时,此channel会被关闭。 - `Err() error`: 如果Done channel被关闭,Err会返回非nil的错误值,说明上下文为什么被取消。 - `Value(key interface{}) interface{}`: 返回与此上下文相关的值,仅用于传递跨API边界的进程内请求数据。 #### 二、`cancelCtx`的创建与取消机制 `cancelCtx`是通过调用`context.WithCancel(parent Context)`函数创建的。这个函数接收一个父上下文`parent`(可以是nil,表示没有父上下文),并返回一个子上下文`ctx`和一个取消函数`cancel`。调用`cancel`函数时,会关闭`ctx`的Done channel,同时取消所有基于此`ctx`创建的子上下文。 ```go ctx, cancel := context.WithCancel(context.Background()) // 在需要的时候,调用cancel来取消上下文 cancel() ``` #### 三、利用`cancelCtx`通知协程终止 在Go的并发编程中,协程的终止通常不是通过直接调用某个函数来完成的,而是通过监听某个信号(如channel的关闭、select语句中的case等)来实现。`cancelCtx`的Done channel正是这样一个机制,它允许我们监听取消信号,并据此来终止协程的执行。 ##### 示例:使用`cancelCtx`终止协程 假设我们有一个任务,需要并发地处理多个数据项,并且我们希望在某个时刻能够取消这些任务。以下是如何使用`cancelCtx`来实现这一功能的示例: ```go package main import ( "context" "fmt" "sync" "time" ) // 模拟的任务处理函数 func processTask(ctx context.Context, id int) { select { case <-time.After(time.Second * 2): // 模拟任务执行需要一些时间 fmt.Printf("Task %d completed\n", id) case <-ctx.Done(): fmt.Printf("Task %d cancelled\n", id) } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) go func(id int) { defer wg.Done() processTask(ctx, id) }(i) } // 假设我们在1秒后决定取消所有任务 time.Sleep(time.Second) cancel() wg.Wait() // 等待所有协程完成 fmt.Println("All tasks have been processed or cancelled.") } ``` 在这个示例中,我们创建了5个协程来模拟并发执行任务。每个协程都通过`select`语句监听两个channel:一个是`time.After`返回的channel,用于模拟任务完成;另一个是`ctx.Done()`,用于监听取消信号。当调用`cancel()`函数时,`ctx.Done()`会被关闭,导致所有监听该channel的协程都接收到取消信号,并打印出取消的信息。 #### 四、`cancelCtx`的注意事项 1. **避免泄漏**:确保在不再需要`cancel`函数时调用它,以释放资源并避免潜在的内存泄漏。在上面的示例中,我们通过`defer cancel()`或在适当的时候显式调用`cancel()`来确保这一点。 2. **父子上下文关系**:当你创建一个`cancelCtx`时,它会成为其父上下文的一个子上下文。取消子上下文不会影响父上下文,但取消父上下文会取消所有基于该父上下文创建的子上下文。 3. **错误处理**:在接收到取消信号后(即`ctx.Err()`返回非nil错误),应该优雅地处理当前任务的终止,包括释放资源、回滚事务等。 4. **与`time.Timer`结合使用**:有时你可能需要在达到某个时间限制时自动取消上下文,这时可以结合使用`time.NewTimer`和`cancelCtx`。不过,注意在任务完成后要停止Timer,以避免资源泄漏。 #### 五、总结 通过`cancelCtx`,Go语言提供了一种优雅的方式来通知并终止协程的执行。它不仅简化了协程生命周期的管理,还提高了代码的清晰度和可维护性。在编写并发程序时,合理利用`context`包,特别是`cancelCtx`,是实现高效、可靠并发控制的关键。
上一篇:
从父节点获得透传值
下一篇:
通知子协程终止执行
该分类下的相关小册推荐:
Go开发权威指南(上)
深入浅出Go语言核心编程(四)
Go Web编程(上)
Go语言从入门到实战
go编程权威指南(四)
Golang修炼指南
GO面试指南
Go Web编程(中)
Go开发权威指南(下)
Golang并发编程实战
企业级Go应用开发从零开始
go编程权威指南(三)