利用 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
rimutuyuan 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 吧? |