go 语言如何关闭正在运行的协程?谢谢

2022-05-07 11:24:29 +08:00
 hkhk366
package main

import (
	"fmt"
	"sync"
	"time"
)

func workRoutine(work chan string, done chan string, wg *sync.WaitGroup) {
	select {
	case donemsg := <-done:
		fmt.Println(donemsg)
	case msg := <-work:
		//这里将有大量工作可能半个小时都未必执行完,这样 done 这个 channel 就无法收到退出信号
		for i := 0; i < 100000; i++ {
			fmt.Println(msg)
			time.Sleep(time.Second)
		}
	case <-time.After(time.Second * 1000):
		fmt.Println("timeout")
	}
	wg.Done()
}
func closeWorkRoutine(done chan string, wg *sync.WaitGroup) {
	//目标是 10 秒后希望能关掉 test 这个协程
	time.Sleep(time.Second * 10)
	done <- "done"
	wg.Done()
}
func main() {
	done := make(chan string)
	work := make(chan string, 1)
	wg := sync.WaitGroup{}
	wg.Add(2)
	work <- "work"
	go workRoutine(work, done, &wg)
	go closeWorkRoutine(done, &wg)
	wg.Wait()
}

请参考上面的代码,我现在有两个协程,一个叫 workRoutine ,另一个叫 closeWorkRoutine ,我的目标是希望 closeWorkRoutine 可以在 10 秒后可以关闭 workRoutine 的协程,但是上面这个代码是无法关闭的,因为 work 过于繁重,永远轮不到执行关闭的时候。请问有什么办法可以直接关闭协程,而无需介意当前协程的状态,最好能像线程那样有一个 ID 我可以直接在外部强制关掉,请问我应该如何做呢,谢谢。

5800 次点击
所在节点    Go 编程语言
37 条回复
rophie123
2022-05-07 11:27:09 +08:00
context
gollwang
2022-05-07 11:28:32 +08:00
楼上正解
codefever
2022-05-07 11:32:46 +08:00
如果想中途 cancel 掉,可以使用 context.WithCancel
rekulas
2022-05-07 11:33:59 +08:00
严格来说是没办法的,只有发生上下文切换的时候你才有机会执行退出逻辑,如果协程阻塞在某个操作你没有办法去关闭
阻塞了 context 什么的都没用
DollarKiller
2022-05-07 11:36:30 +08:00
关闭掉的,context 只是 channel 发一个通知
mainjzb
2022-05-07 11:44:31 +08:00
没办法
hejw19970413
2022-05-07 11:53:27 +08:00
context 正解
brader
2022-05-07 11:59:39 +08:00
添加事件机制? work 里面的循环,每干完一轮活,就检查是否有退出事件?
hejw19970413
2022-05-07 12:09:31 +08:00
package main

import (
"context"
"time"
)

var work = make(chan struct{})

func workRoutine(ctx context.Context) {

f1 := func(ctx context.Context) {
select {
case <-ctx.Done():
default:

}
}
for {
select {
case <-ctx.Done():
break
case <-work:
// 任务分解 , 分别限时
c1, _ := context.WithTimeout(ctx, 10*time.Second)
f1(c1)
}
}
}
func main() {
c, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
go workRoutine(c)
select {}
}

不知道这样能不能满足你的要求
FreeEx
2022-05-07 12:12:46 +08:00
执行每一个子任务的时候加上时间限制,并且没执行完一次子任务就检查一下是否需要退出。
wunonglin
2022-05-07 12:16:41 +08:00
func workRoutine(work chan string, done chan string, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case donemsg := <-done:
fmt.Println(donemsg)
return
case <-time.After(time.Second * 1000):
fmt.Println("timeout")
return
case msg := <-work:
fmt.Println(msg)
time.Sleep(time.Second)
fmt.Println("work done")
}
}
}

这样的?
ilylx2008
2022-05-07 12:20:39 +08:00
`
for (i:=0;i<100000;i++){
select {
case donemsg := <-done:
//stop
default:
//do something
}
}
Jessun
2022-05-07 12:21:01 +08:00
context 要看怎么用,我的建议是将 context 继续向下传递。

"//这里将有大量工作可能半个小时都未必执行完,这样 done 这个 channel 就无法收到退出信号"
这里如果是一个执行很长的代码,从外部你无法干掉的。将 context 继续传入这个函数,以及它的子函数,即 context 来控制整个调用链路上的超时。

根据目前信息,就这个思路最简单了。
NIYIKI
2022-05-07 13:50:46 +08:00
关闭不了的
zhangfuguan
2022-05-07 14:15:08 +08:00
```go

package main

import (
"log"
"sync"
"time"
)

var wg sync.WaitGroup

func worker(quit <-chan int) {
defer wg.Done()
for {
select {
case <-quit:
log.Printf("收到退出信号")
return // 必须 return ,否则 goroutine 是不会结束的
default:
log.Println("loading...")
time.Sleep(time.Second * 1)
}
}
}

func main() {
quit := make(chan int) // 退出通道

wg.Add(5)

go worker(quit) // work 1
go worker(quit) // work 2
go worker(quit) // work 3
go worker(quit) // work 4
go Done(quit) // 结束所有任务

wg.Wait()
}

func Done(ch chan int) {
defer wg.Done()

time.Sleep(time.Second * 10)
close(ch)
}

```

这样吗?
walleL
2022-05-07 14:41:18 +08:00
//这里将有大量工作可能半个小时都未必执行完,这样 done 这个 channel 就无法收到退出信号
for i := 0; i < 100000; i++ {
fmt.Println(msg)
time.Sleep(time.Second)
}
------
只能将上面这段长时间执行的操作分解,并在步骤间判断是否需要退出。上面已经有朋友讲过了
keepeye
2022-05-07 14:58:24 +08:00
楼上说 context 的真的认真审题了吗...

不管是自定义的 done 还是用 context 包,必须在某个位置读取信号,我没见过从外部强制销毁 goroutine 的办法
fighterlyt
2022-05-07 15:39:35 +08:00
context 只不过是简化了之前传入控制 channel 的方法,如果底层没有 等待+检查+执行的机制,开弓没有回头箭
tianyou666shen
2022-05-07 15:48:52 +08:00
你这等于要在外部强制 kill -9 杀掉这个 goroutine 的效果?
考虑通过退出主协程的方式强制让子协程被杀死这种方式吗.
比方主协程开启 workRoutine 子协程以后,监听信号,当你输入信号时,主协会直接退出,同时子协程也被退出
stevefan1999
2022-05-07 15:49:14 +08:00
沒有辦法直接關閉 只能提醒可以關閉 直到協程提起接受關閉才可以 這裡涉及一部分操作系統線程排程問題很複雜

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

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

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

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

© 2021 V2EX