背景是这样的:
用户在页面点击一个执行,这时候会有一个 goroutine job 在 running 中,
那么用户再次点击停止执行,这次请求如何去关闭上次正在运行的 goroutine ?
1
Sendya 2022-09-28 11:26:36 +08:00 via Android
创建 job 的时候弄个 context.WithCancal ,在需要的时候调用 cancel 取消掉即可
|
2
caicaiwoshishui OP @Sendya 这是 2 次不同请求,第二次请求怎么拿到第一次的 goroutine ,goroutine 对象能保存嘛
|
3
MoYi123 2022-09-28 11:31:23 +08:00
用 context.WithCancal
还有看 job 是什么类型的, 如果是纯 cpu 计算的, 你要在每几次循环里面确认是否要退出, 如果是等待网络 io 之类的,需要 select <- ctx.Done() 总之没有那种不侵入代码的写法. |
4
rimutuyuan 2022-09-28 11:39:35 +08:00 2
可以用{jobid: cancelFunc}的格式保存,需要结束时获取 cancelFunc 执行一下就行了
|
5
plutome 2022-09-28 11:46:06 +08:00
@rimutuyuan 楼上正解, 注意下 map 的并发安全问题,以及锁,还有就是加一个定时清理机制就可以解决需求了.
|
6
Maboroshii 2022-09-28 11:47:28 +08:00 via Android 1
go 不提供这个,需要自己在逻辑里面检测是否需要退出。context 的原理就是传一个 chan 给函数里面,函数自己判断 cancel 是否被调用。
|
7
paccco 2022-09-28 11:51:11 +08:00
没有办法直接关闭指定的 goroutine ,可以采用曲线救国的方式,在你 goroutine 的业务中做特殊处理通知让其自行结束
|
8
caicaiwoshishui OP @rimutuyuan 感谢 确实是个好思路
|
9
caicaiwoshishui OP @Maboroshii 谢谢,主要是 2 次请求是不同的 context ,第二次请求如何拿到第一次的 context
|
10
bruce0 2022-09-28 12:21:21 +08:00
@caicaiwoshishui 像楼上说的那样, 用个全局 map 保存一下(记得加锁), 第一次开始 goroutine 的时候写到 cookie 里一个 key,第二次通过 cookie 里的 key 查询 map, 能找到 就取消掉
|
11
dzdh 2022-09-28 12:32:21 +08:00 2
生成 jobid 传给前端。点击取消时把 jobid 传回来
或者 redis 或其他存储 点击任务(必定有个 id),redis.set(id, running) 。协程中再开个协程查 redis.get(id)不等于 running 退出 |
12
dzdh 2022-09-28 12:34:01 +08:00
每次点击
if redis.get(id) == running ; redis.set(id,quit) else go func(){redis.set(id,running) |
13
lanlanye 2022-09-28 13:16:49 +08:00
就是 web 开发中的 session 解决的问题,无非是把 context 存在里面罢了
|
14
frank1256 2022-09-28 14:15:54 +08:00
@caicaiwoshishui context 持久化就行了,放 redis 之类的缓存中间件就行了
|
15
useben 2022-09-28 21:52:16 +08:00
盲猜是任务管理列表
|
16
777777 2022-09-29 14:47:17 +08:00
@frank1256 ctx 的 cancel 是个函数不能放 redis 里。建议 11 楼的方法,用 cancel 的化不能无状态动态扩展
|
17
dzdh 2022-09-29 15:40:13 +08:00
@777777 #16
有一种情况 拿 ffmpeg 转换视频来说。可能是这样的 go func(ctx, video-source) { for { select { <ctx.done; 在哪一步启动 ffmpeg 呢 |
18
guanhui07 2022-09-30 23:19:31 +08:00
```golang
func main() { ch := make(chan struct{}) ctx, cancel := context.WithCancel(context.Background()) go func(ctx context.Context) { for { select { case <-ctx.Done(): ch <- struct{}{} return default: fmt.Println("test11111...") } time.Sleep(500 * time.Millisecond) } }(ctx) go func() { time.Sleep(3 * time.Second) cancel() }() <-ch fmt.Println("结束") } ``` 是这样吗 |
19
caicaiwoshishui OP @777777 目前是按 11 楼的做法
|
20
caicaiwoshishui OP @777777 但是要结合 ctx cancel ,不然一个 job 如果有多个逻辑,比如 job 里面有执行 a,b,c 三个逻辑块,那么需要把三个逻辑都封装成函数,并且传入 ctx ;当执行第一个逻辑块 a 的时候,收到 ctx cancel ,这个 job 就会退出,并且不会往下执行
|