
利用 context.WithCancel()来终止协程,但存在一个问题:cancel()执行后,必须等 for 循环执行完毕 goroutine 才退出。
for { select { case <-Ctx.Done(): return default: for i := 0; i < 100; i++ { // 业务逻辑 } } } 需求为:cancel()后协程立马退出不再执行后面的循环。
目前我的解决方法:单独起一个协程用来监听退出信号,然后通过全局变量通知业务逻辑循环退出。
flag := false go func() { for { select { case <-Ctx.Done(): flag = true return } }() for i:=0; i < 100; i++ { if flag { return } // 业务逻辑 } 请问最佳实践应该是如何退出?
for i := 0; i < 100; i++ { select { case <-Ctx.Done(): return default: // 业务逻辑 } } 感谢大家的回答,每次循环都检测一次退出信号就好了。
延伸出另一个问题:用户取消任务,如何让default里的业务逻辑立马终止?
看了一下os.exec.cmd.Start()的源码
if c.ctx != nil { c.waitDOne= make(chan struct{}) go func() { select { case <-c.ctx.Done(): c.Process.Kill() case <-c.waitDone: } }() } 另起一个goroutine监听cancel(),然后kill掉执行的process。
类比本问题,一个协程执行任务,一个协程监听任务的cancel(),任务取消直接kill掉执行任务的协程且监听协程也退出。
1 dream10201 2021-08-30 15:24:09 +08:00 瞎想的,你把 select case 放到 for 循环里面不行? |
2 dream10201 2021-08-30 15:25:34 +08:00 for { for i := 0; i < 100; i++ { select { case <-Ctx.Done(): return default: // 业务逻辑 } } } |
3 14v45mJPBYJW8dT7 2021-08-30 15:26:40 +08:00 ``` for { for i := 0; i < 100; i++ { select { case <-Ctx.Done(): return default: // 业务逻辑 } } }``` |
4 BBCCBB 2021-08-30 15:26:59 +08:00 这种可以在 for 里每次检测 ctx.Done()... 不过不是最佳实践. 和你图中这个方法差不多,, 不过不知道你图中这个方法有没有变量可见性的问题? 导致 flag=true 后在当前协程并不可见? |
6 777777 OP @dream10201 这样有问题吧,如果不调用 cancel(),最外面 for 循环不会退出。 |
7 sadfQED2 2021-08-30 16:06:43 +08:00 via Android 这个问题我在 java 里面也研究过,最后结果就是无解,只能业务代码里面每次循环都判断 |
8 xx6412223 2021-08-30 16:13:13 +08:00 直接退出就不是合理的设计。 |
9 caryqy 2021-08-30 16:24:47 +08:00 执行前检查 Done, 执行后提交前再次检查 Done,根据结果来收尾。 考虑的粒度即使再细都会碰到 执行中取消 这个问题,所以到某个程度之后就 禁止用户取消 |
10 haochen2 2021-08-30 17:11:31 +08:00 如果业务逻辑超时运行或着阻塞,根本走不到 ctx.Done 那个 case 的地方 |
11 cpstar 2021-08-30 17:34:57 +08:00 循环第二位置用 i<100&&ctx.done() 不好么?还非得一个 switch-case ? 至于楼上说的业务阻塞,那就得放到业务里边继续判断了。 多线程运转,直接干掉线程,哈 |
12 MidGap 2021-08-30 17:46:36 +08:00 runtime.Goexit() |
15 s4nd 2021-08-30 18:57:42 +08:00 老哥,你这头像是认真的吗,nft 大佬 |
17 cheng6563 2021-08-31 09:15:33 +08:00 @sadfQED2 Java 一顿 Interrupt 就行了,只不过没人管 InterruptedException 倒是真的 |
18 codeface 2021-08-31 13:05:14 +08:00 在 context 传递给业务逻辑 https://gist.github.com/icodeface/89aaf03d763c11ed204c1016cca8ac30 |
19 caoyouming 2021-09-01 12:09:53 +08:00 你这里的 c 表示的是什么呀? |
20 ltf127001 2021-09-01 17:55:42 +08:00 @caoyouming 根据我在菜鸟教程学了两天 go 的经验,这个应该是个通道( channel ) |
21 caoyouming 2021-09-01 18:30:13 +08:00 @ltf127001 #20 if c.ctx != nil { c.waitDOne= make(chan struct{}) go func() { select { case <-c.ctx.Done(): c.Process.Kill() case <-c.waitDone: } }() } 这里的 c 应该不是 channel 吧? |