Golang Go语言中如何非侵入式的停止一个 goroutine

发布于 1周前 作者 itying888 来自 Go语言

Golang Go语言中如何非侵入式的停止一个 goroutine
是的,我的意思有点类似 kill 它,而不是退出它。
我正在为实现这样一个功能苦恼,标准库中的 context, sync.WaitGroup 都只是辅助开发者来关闭或等待一个 goroutine 关闭,而我想要实现一个:func executeFunc(myFunc, timeout)的方式,当 timeout 到达时,直接 kill 这个 goroutine,而不需要传任何与任务函数无关的参数来侵入 goroutine。
在 Stack Overflow 上的答案是:没有这种方法,除非 Os.Exit to quit whole program.
大家来发表一下意见啊~~~


更多关于Golang Go语言中如何非侵入式的停止一个 goroutine的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

22 回复

Select 可以用来实现超时

更多关于Golang Go语言中如何非侵入式的停止一个 goroutine的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


没有这种东西,你这种设计才是粗暴的侵入吧

你可以用多个程序,然后用 exec.Cmd.Process.Kill

既然都设置 timeout 了那就用 context.Cancel() 呗。也加不了几行。

标准的方式用 ctx 控制超时啊

协程本身就不支持这样的,只能协程自己退出。你想结束它唯一办法就是 kill

谢谢你提出的方法,不过这不是一种轻的实现方式没有满足我的想法。
不过请问下我的方式侵入什么了?怎么个侵入法?



谢谢回复,不过你们没有理解我的意思

但是 golang 不支持 kill 哇,或者说我不知道为什么 go 官方不提供 kill 方法。

侵入了 goroutine 的执行

从外部是没法控制的,你可以想下怎样从内部控制,让函数里面不得不调用一些检查

goroutine 不是线程啊,而 go 又没有 vm 给你做协程的控制,协程就是有用户(开发者)自己用代码控制的啊。

你这个想法太暴力了,最好是协程能自己控制,超时到的时候释放各种资源然后退出协程

老哥点破我了,确实协程的概念是开发者调度的。好吧,也许是 golang 的 go func()这种极简的设计方式让我把对线程的思考方式代入协程里面了。

https://tip.golang.org/doc/faq#no_goroutine_id

因为官方认为 goroutine 不应该有 id,那自然就不会有从外部控制的方法

3q,我先看看

goroutine 不是协程,开发者也没法控制调度。

就算是操作系统线程,kill 系统调用也是发一个信号过去,退出不退出,线程本身也有一定选择权的,没有这么粗暴的

一个线程并不是随时都可以安全退出的,在某些状态下,一个线程并不能安全地退出,如果这时它被强行结束,可能会破坏程序状态。所以需要你在线程中的“安全地带”处检测是否要退出线程并主动退出,而不能直接从外部 kill 掉它。

了解,你说的是线程内使用了锁或者打开的文件资源这些情况吧。因为 kill 是 OS 提供的,调用是开发者执行的,所以开发者在 kill 的时候必须考虑到这些情况。这个就不在帖子讨论的主题范围了

。。。楼主问个问题还这么有性格

线程 /进程
外部发送 kill 信号给它,它会在内部检测到,然后进行合适的处理,只是被封装好了你看不到这个而已 —> 这个跟 context 解决 goroutine 是完全一致的。

那么就只剩下 kill -9 的问题了:
线程 /进程是有独立的资源的,强制终结之后操作系统会回收处理。不会影响其他程序运行。
但是 goroutines 里有大量的共享资源,变量、内存、引用地址等等,根本无法辨别,所以就无法处理了。所以没有 kill -9

第一段话可以理解。 但是线程也可以使用全局变量,也可以做一些锁行为,这时候 kill 也会有问题,kill 与否由开发者决定。goroutine 拥有的资源线程一样有啊,你没有说到点子上。

协程不是内核态的,不像进程和线程,分配了 pid,而且是应用层分配资源和调度的,所以没办法 kill

在Golang中,实现非侵入式地停止一个goroutine通常意味着我们不想在goroutine内部直接添加停止逻辑(如检查某个全局变量或通道信号)。一个常见且推荐的方法是使用上下文(context)来传递停止信号。

上下文(context.Context)在Go中用于在API边界之间传递截止日期、取消信号以及其他请求范围内的值。通过上下文,你可以优雅地通知goroutine进行清理和退出。

下面是一个简单的示例,演示如何使用上下文来非侵入式地停止一个goroutine:

package main

import (
    "context"
    "fmt"
    "time"
)

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Goroutine stopped")
            return
        default:
            fmt.Println("Working...")
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go worker(ctx)

    time.Sleep(5 * time.Second)
    cancel() // 发送停止信号

    time.Sleep(1 * time.Second) // 确保worker有足够时间打印停止信息
}

在这个例子中,worker函数在一个无限循环中工作,但每次循环都会检查上下文的Done通道是否已关闭。当主函数中的cancel函数调用时,Done通道会被关闭,worker函数接收到信号后优雅地停止。这种方法避免了在goroutine内部直接编写停止逻辑,从而实现了非侵入式的停止。

回到顶部