当前位置: 首页 > article >正文

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

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

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

问题描述

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

package main

import (
	"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() {
	// 创建一个可以取消的 context
	ctx, 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 main

import (
	"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() {
	// 创建一个可以取消的 context
	ctx, 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() 通道,我们可以确保任务能够及时响应取消信号并优雅地退出。这对于构建健壮和可靠的并发应用程序至关重要。


http://www.kler.cn/a/414109.html

相关文章:

  • 在 Linux 系统中根据pid查找软件位置
  • 【mac】终端左边太长处理,自定义显示名称(terminal路径显示特别长)
  • 面试手撕题积累
  • 14、保存与加载PyTorch训练的模型和超参数
  • Java全栈开发实战:相亲网站开发教程
  • DDR3与MIG IP核详解(一)
  • MySQL安装与卸载(linux)
  • docker查询是否运行
  • 《Unity Shader 入门精要》高级纹理
  • 网络编程中的字节序函数htonl()、htons()、ntohl()和ntohs()
  • C# 7.1 .Net Framwork4.7 VS2017环境下,方法的引用与调用
  • InstructGPT——AI 模型的对齐革命
  • 【插入排序】:直接插入排序、二分插入排序、shell排序
  • Python练习47
  • cesium 3dtile ClippingPlanes 多边形挖洞ClippingPlaneCollection
  • 同三维T80005JEHVA视频解码器
  • 算法知识-14-递归
  • VTK的基本概念(一)
  • 【Qt】QSettings类实现配置信息长期保存(掉电不擦除)
  • 【开发商城系统】
  • Redis开发03:常见的Redis命令
  • pandas 数据分析流程
  • 鸿蒙HarmonyOS学习笔记(6)
  • Opencv+ROS实现摄像头读取处理画面信息
  • Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
  • OGRE 3D----5. OGRE和QML事件交互