Golang Go语言中怎么 Cancel 一个非循环子协程
业务背景是有一个每隔 10 秒执行一次的任务,这个任务会拿很多数据还有分析耗时会长一点,处理完后会给 chan 发数据.
然后数据修改的时候会调用 context 的 cancel 去停止协程, 但是只能停止 10s 定时任务这个协程,不能把还在处理这个任务也停止掉,会出现脏数据传到 chan 里
有没有什么好的方法能让他在执行一半的时候直接被 cancel 掉
Golang Go语言中怎么 Cancel 一个非循环子协程
一个协程,同时 wait context.cancel 和那个 worker 的处理结果。拿到 cancel 不处理,拿到处理结果时写入 chan
更多关于Golang Go语言中怎么 Cancel 一个非循环子协程的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
没有优雅的实现。
其他语言可以给拿数据的连接设置 read timeout ,或者用子进程来处理。
但是 Go 把连接给封装了,大部分数据库的接口是同步的,你没法同时监听 fd 可读和定时器超时。
子进程要传数据需要用 pipe ,但是它的 Read()/Write() 是同步的,需要再起个协程去读写,才能和监听子进程退出和超时一起 select 。而且 Go 的 runtime 和第三方库可能都跑了一些子线程,fork 时并不会复制这些子线程,导致有时候会崩溃。
或者可以简化一点,无需立马取消掉;而是在将数据写入 chan 之前,先做个判断,任务正常则将数据写入 chan ;反之则跳过写数据的步骤,直接结束任务。
传递上下文,在需要的位置使用,比如在这里避免脏数据的话,向 chan 写数据之前的某个地方检查上下文是否超时或者已经 canceled
```go
select {
case <- ctx.Done():
default:
}
这样其实还是得让他走完流程, 旧数据都没有做并发支持只用的原生 map
“这个任务会拿很多数据还有分析耗时会长一点”;把这个任务拆分成小任务,然后每个小任务中间检查 ctx done
这问题我研究过:没有办法杀死正在运行的协程,只能把你的任务分为多个多个小任务然后检查 ctx
这样写也太丑了啊😵
正常是这么做的,一般会在循环中加入检查 ctx.Done, 比如说在从流中读取数据,读取下一条数据等等,像 lysS 说的将任务实现为单一功能的小任务;
ctx 就是这样的,在用户态,不能抢占协程,只能协作
在 Go 语言中,取消一个非循环子协程(goroutine)通常通过 context
包来实现。context
包提供了一种方法来设置截止日期、同步取消信号以及传递请求范围内的值(如超时、取消信号等)给多个 goroutine。
以下是一个基本示例,展示了如何使用 context
来取消一个非循环子协程:
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Goroutine canceled:", ctx.Err())
return
case <-time.After(5 * time.Second):
fmt.Println("Goroutine finished work")
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
// 模拟一些主协程的工作
time.Sleep(2 * time.Second)
// 取消子协程
cancel()
// 等待一段时间以确保取消信号生效
time.Sleep(1 * time.Second)
fmt.Println("Main function ends")
}
在这个例子中,worker
函数在一个新的 goroutine 中运行,并监听 ctx.Done()
通道。当主协程调用 cancel()
函数时,ctx.Done()
会被关闭,并且 ctx.Err()
会返回一个取消错误。worker
goroutine 检测到这个信号后,会提前退出。
这种方法非常适用于需要控制 goroutine 生命周期的场景,例如处理 HTTP 请求、执行定时任务等。