使用 Go 语言中的 Context 取消协程执行

news/2024/12/13 13:14:51

使用 Go 语言中的 Context 取消协程执行

在 Go 语言中,协程(goroutine)是一种轻量级的线程,非常适合处理并发任务。然而,如何优雅地取消正在运行的协程是一个常见的问题。本文将通过一个具体的例子来展示如何使用 context 包来取消协程的执行,特别是处理嵌套任务中的取消问题。

问题描述

假设我们有一个长时间运行的任务,该任务包含一个外层循环和一个内层任务。我们需要在外层循环接收到取消信号时,能够立即终止内层任务。以下是一个示例代码:

package mainimport ("context""fmt""time"
)// longRunningTask 是一个模拟长时间运行的任务。
func longRunningTask(ctx context.Context) {for {select {case <-ctx.Done(): // 监听 ctx.Done() 以获取取消信号fmt.Println("任务被取消:", ctx.Err())return // 接收到取消信号后退出default:currentTime := time.Now().Format("2006-01-02 15:04:05") // 获取并格式化当前时间fmt.Printf("任务进行中... 当前时间:%s\n", currentTime)for {fmt.Printf("111")time.Sleep(1 * time.Second) //}}}
}func main() {// 创建一个可以取消的 contextctx, cancel := context.WithCancel(context.Background())// 启动一个新的 goroutine 执行任务go longRunningTask(ctx)// 模拟一段时间后取消任务time.Sleep(3 * time.Second)fmt.Println("取消任务...")cancel() // 发送取消信号// 等待一段时间让任务有时间处理取消信号并退出time.Sleep(10 * time.Second)
}

在这个示例中,当我们取消任务时,外层循环会接收到取消信号并退出,但内层循环会继续运行,因为我们没有在内层循环中检查取消信号。

解决方案

为了确保内层任务也能响应取消信号,我们需要在内层任务中也检查 ctx.Done() 通道。以下是修改后的代码:

package mainimport ("context""fmt""time"
)// longRunningTask 是一个模拟长时间运行的任务。
func longRunningTask(ctx context.Context) {for {select {case <-ctx.Done(): // 监听 ctx.Done() 以获取取消信号fmt.Println("任务被取消:", ctx.Err())return // 接收到取消信号后退出default:currentTime := time.Now().Format("2006-01-02 15:04:05") // 获取并格式化当前时间fmt.Printf("任务进行中... 当前时间:%s\n", currentTime)// 启动内层任务runInnerTask(ctx)}}
}// runInnerTask 是一个模拟内层长时间运行的任务。
func runInnerTask(ctx context.Context) {for {select {case <-ctx.Done(): // 内层任务也监听 ctx.Done()fmt.Println("内层任务被取消:", ctx.Err())return // 接收到取消信号后退出default:fmt.Printf("111")time.Sleep(1 * time.Second)}}
}func main() {// 创建一个可以取消的 contextctx, cancel := context.WithCancel(context.Background())// 启动一个新的 goroutine 执行任务go longRunningTask(ctx)// 模拟一段时间后取消任务time.Sleep(3 * time.Second)fmt.Println("取消任务...")cancel() // 发送取消信号// 等待一段时间让任务有时间处理取消信号并退出time.Sleep(10 * time.Second)
}

解释

外层循环:

外层循环使用 select 语句来监听 ctx.Done() 通道。如果接收到取消信号,任务会打印一条消息并退出。

内层任务:

内层任务也使用 select 语句来监听 ctx.Done() 通道。如果接收到取消信号,内层任务会打印一条消息并退出。

通过这种方式,我们可以确保无论是在外层循环还是内层任务中,任务都能响应取消信号并优雅地退出。

总结

在 Go 语言中,使用 context 包来管理协程的生命周期是非常重要的。通过在每个需要响应取消信号的地方检查 ctx.Done() 通道,我们可以确保任务能够及时响应取消信号并优雅地退出。这对于构建健壮和可靠的并发应用程序至关重要。


https://dhexx.cn/news/show-5466354.html

相关文章

【第三讲】Spring Boot 3.4.0 新特性详解:增强的配置属性支持

Spring Boot 3.4.0 版本在配置属性的支持上进行了显著增强&#xff0c;使得开发者能够更灵活地管理和使用应用程序的配置。新的特性包括对配置属性的改进、类型安全增强、以及对环境变量的更好支持。这些改进旨在提升开发效率和代码可读性&#xff0c;同时简化配置过程。本文将…

MySQL 启动失败问题分析与解决方案:`mysqld.service failed to run ‘start-pre‘ task`

目录 前言1. 问题背景2. 错误分析2.1 错误信息详解2.2 可能原因 3. 问题排查与解决方案3.1 检查 MySQL 错误日志3.2 验证 MySQL 配置文件3.3 检查文件和目录权限3.4 手动启动 MySQL 服务3.5 修复 systemd 配置文件3.6 验证依赖环境 4. 进一步优化与自动化处理结语 前言 在日常…

vue监听键盘事件

在实际应用中&#xff0c;很多时候我们需要监听键盘事件&#xff0c;在vue项目中该如何监听呢&#xff1f; 在mounted中新增监听事件 mounted() {this.dom document.getElementById("dialogHTTP");// 监听键盘抬起事件this.dom.addEventListener("keyup"…

安卓-碎片的使用入门

1.碎片(Fragment)是什么 Fragment是依赖于Activity的&#xff0c;不能独立存在的,是Activity界面中的一部分&#xff0c;可理解为模块化的Activity,它能让程序更加合理和充分地利用大屏幕的空间&#xff0c;因而在平板上应用得非常广泛. Fragment不能独立存在&#xff0c;必须…

上传镜像docker hub登不上和docker desktop的etx4.vhdx占用空间很大等解决办法

平时使用docker一般都在Linux服务器上&#xff0c;但这次需要将镜像上传到docker hub上&#xff0c;但是服务器上一直无法登录本人的账号&#xff0c;&#xff08;这里的问题应该docker 网络配置中没有开代理的问题&#xff0c;因服务器上有其他用户使用&#xff0c;不可能直接…

Mac——鼠标增强插件Mos

功能说明&#xff1a; 能够解决鼠标断续、不灵敏等鼠标问题。 下载地址&#xff1a; Mac——鼠标增强插件Mos

38 基于单片机的宠物喂食(ESP8266、红外、电机)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;采用L298N驱动连接P2.3和P2.4口进行电机驱动&#xff0c; 然后串口连接P3.0和P3.1模拟ESP8266&#xff0c; 红外传感器连接ADC0832数模转换器连接单片机的P1.0~P1.…

无星的微前端之旅(四)——qiankun线上服务代理到本地

这个方式其实是我在上家公司的时候体验过&#xff0c;觉得确实很有意思。 所以这里来逆推一下实现方式。 解决了什么痛点 1.开发一个模块&#xff0c;需要启动2-3个项目 在微前端的开发过程中&#xff0c;如果我们要在主应用中看效果&#xff0c;我们至少需要启动一个主应用&am…

redis cluster 3主3从部署方案

文章目录 1 Redis Cluster 介绍1 Redis cluster 架构2 Redis cluster的工作原理2.1 数据分区2.2 集群通信2.3 集群伸缩2.3.1 集群扩容2.3.2 集群缩容 2.4 故障转移2.4.1 主观下线2.4.2 客观下线 3 Redis Cluster 部署架构说明3.1 部署方式介绍3.2 实战案例&#xff1a;基于Redi…

Envoy 源码解析(一):Envoy 整体架构、Envoy 的初始化

本文基于 Envoy 1.31.0 版本进行源码学习 1、Envoy 整体架构 1&#xff09;、核心组件 Envoy 包含以下四个核心组件&#xff1a; Listener&#xff08;监听器&#xff09;&#xff1a;定义了 Envoy 如何处理入站请求。一旦连接建立&#xff0c;请求会被传递给一组过滤器进行处…