请教一个 goroutine 在运行中,如何关闭的问题

2022-09-28 11:15:50 +08:00
 caicaiwoshishui

背景是这样的:

用户在页面点击一个执行,这时候会有一个 goroutine job 在 running 中,

那么用户再次点击停止执行,这次请求如何去关闭上次正在运行的 goroutine ?

2091 次点击
所在节点    Go 编程语言
20 条回复
Sendya
2022-09-28 11:26:36 +08:00
创建 job 的时候弄个 context.WithCancal ,在需要的时候调用 cancel 取消掉即可
caicaiwoshishui
2022-09-28 11:31:11 +08:00
@Sendya 这是 2 次不同请求,第二次请求怎么拿到第一次的 goroutine ,goroutine 对象能保存嘛
MoYi123
2022-09-28 11:31:23 +08:00
用 context.WithCancal

还有看 job 是什么类型的, 如果是纯 cpu 计算的, 你要在每几次循环里面确认是否要退出,
如果是等待网络 io 之类的,需要 select <- ctx.Done()

总之没有那种不侵入代码的写法.
rimutuyuan
2022-09-28 11:39:35 +08:00
可以用{jobid: cancelFunc}的格式保存,需要结束时获取 cancelFunc 执行一下就行了
plutome
2022-09-28 11:46:06 +08:00
@rimutuyuan 楼上正解, 注意下 map 的并发安全问题,以及锁,还有就是加一个定时清理机制就可以解决需求了.
Maboroshii
2022-09-28 11:47:28 +08:00
go 不提供这个,需要自己在逻辑里面检测是否需要退出。context 的原理就是传一个 chan 给函数里面,函数自己判断 cancel 是否被调用。
paccco
2022-09-28 11:51:11 +08:00
没有办法直接关闭指定的 goroutine ,可以采用曲线救国的方式,在你 goroutine 的业务中做特殊处理通知让其自行结束
caicaiwoshishui
2022-09-28 11:52:55 +08:00
@rimutuyuan 感谢 确实是个好思路
caicaiwoshishui
2022-09-28 11:54:08 +08:00
@Maboroshii 谢谢,主要是 2 次请求是不同的 context ,第二次请求如何拿到第一次的 context
bruce0
2022-09-28 12:21:21 +08:00
@caicaiwoshishui 像楼上说的那样, 用个全局 map 保存一下(记得加锁), 第一次开始 goroutine 的时候写到 cookie 里一个 key,第二次通过 cookie 里的 key 查询 map, 能找到 就取消掉
dzdh
2022-09-28 12:32:21 +08:00
生成 jobid 传给前端。点击取消时把 jobid 传回来

或者 redis 或其他存储 点击任务(必定有个 id),redis.set(id, running) 。协程中再开个协程查 redis.get(id)不等于 running 退出
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)
lanlanye
2022-09-28 13:16:49 +08:00
就是 web 开发中的 session 解决的问题,无非是把 context 存在里面罢了
frank1256
2022-09-28 14:15:54 +08:00
@caicaiwoshishui context 持久化就行了,放 redis 之类的缓存中间件就行了
useben
2022-09-28 21:52:16 +08:00
盲猜是任务管理列表
777777
2022-09-29 14:47:17 +08:00
@frank1256 ctx 的 cancel 是个函数不能放 redis 里。建议 11 楼的方法,用 cancel 的化不能无状态动态扩展
dzdh
2022-09-29 15:40:13 +08:00
@777777 #16

有一种情况 拿 ffmpeg 转换视频来说。可能是这样的

go func(ctx, video-source) { for { select { <ctx.done;

在哪一步启动 ffmpeg 呢
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("结束")
}
```

是这样吗
caicaiwoshishui
2022-10-06 19:40:36 +08:00
@777777 目前是按 11 楼的做法
caicaiwoshishui
2022-10-06 19:45:24 +08:00
@777777 但是要结合 ctx cancel ,不然一个 job 如果有多个逻辑,比如 job 里面有执行 a,b,c 三个逻辑块,那么需要把三个逻辑都封装成函数,并且传入 ctx ;当执行第一个逻辑块 a 的时候,收到 ctx cancel ,这个 job 就会退出,并且不会往下执行

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/883493

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX