怎样从抱睡变成自主入睡 怎样从csgo退出

本文探讨了在Go语言中,如何从协程的调用堆栈深处强制退出当前协程。主要介绍了两种方法:使用`runtime.Goexit()`来立即终止当前协程并执行延迟函数,以及利用`p anic`和`recover`模拟异常处理来中断协程执行。文章将通过示例代码详细说明这两种方法的使用、注意事项及其适用场景,并强调`panic`与`recover`结合使用的必要性,导致程序崩溃。 p>
在Go语言的并发编程中,协程(goroutine)是轻量级的执行单元。有时,我们可能需要在协程执行过程中,从其堆栈调用的任意深度(例如,从一个很深的函数中)强制终止当前协程的执行。这与传统语言中的异常处理(如 Java 的 throw 或 Python 的 raise)有些类似,但 Go 提供了其特有的机制来处理这种情况。本文将详细介绍两个主要方法:runtime.Goexit()和panic/recover。使用 runtime.Goexit() 终止协程
runtime.Goexit()是Go运行时提供的一个函数,用于立即终止当前正在执行的goroutine。当runtime.Goexit()被调用时,它会执行当前goroutine中所有已注册的延迟函数(defer),然后终止该goroutine。需要注意的是,runtime.Goexit()不会影响其他goroutine的执行,也不会导致进程顺序崩溃。
工作原理:当runtime.Goexit()被调用时,它会停止:当前goroutine的执行。执行当前goroutine中所有已注册的defer函数。将控制权返回给调度器,调度器会安排其他可运行的goroutine。
示例代码:
立即进入“豆包AI人工智官网入口”;
立即学习“豆包AI人工智能在线问答入口”;package mainimport ( quot;fmtquot; quot;runtimequot; quot;timequot;)// foo 函数尝试退出协程func foo() { fmt.Println(quot;进入 foo()quot;) // 这里调用runtime.Goexit() fmt.Println(quot;准备从 foo() 退出协程...quot;) runtime.Goexit() // 这行代码将不会被执行 fmt.Println(quot;这行代码在runtime.Goexit()之后,不会被执行。quot;)}// bar 函数调用 foounc bar() { fmt.Println(quot;进入 bar()quot;) defer fmt.Println(quot;bar() 的 defer 被执行quot;) foo() fmt.Println(quot;这行代码在 foo() 之后,不会被执行。
quot;)}// goroutine 函数是我们的主协程逻辑 func myGoroutine() { fmt.Println(quot;myGoroutine 开始运行quot;) defer fmt.Println(quot;myGoroutine 的 defer 被执行quot;) for i := 0; i lt; 5; i { fmt.Printf(quot;myGoroutine 循环 d\nquot;, i) bar() fmt.Printf(quot;myGoroutine 循环 d 结束\nquot;, i) // 这行代码在第一次循环后不会被执行 time.Sleep(100 * time.Millisecond) } fmt.Println(quot;myGoroutine 正常结束quot;) // 这行代码不会被执行}func main() { fmt.Println(quot;main 协程开始quot;) go myGoroutine() // 让main协程保持运行时间,以便观察myGoroutine一段时间 的行为 time.Sleep(1 * time.Second) fmt.Println(quot;main 协程结束quot;)}登录后复制
输出示例:main 协程开始myGoroutine 开始运行 myGoroutine 循环 0进入 bar()进入 foo() 准备从 foo() 退出协程...bar() 的 defer 被执行 myGoroutine 的 defer 被执行main 协程结束登录后复制
从输出中可以看出,当foo()中调用runtime.Goexit()后,foo()和bar()中runtime.Goexit()之后的代码都不会执行,但bar()和myGoroutine()中的defer函数都得到了执行。myGoroutine立即终止,不会进入下一次循环。
注意事项:runtime.Goexit()只终止当前协程,不会影响主程序或其他协程。它会保证所有延迟函数被执行,这对于资源清理非常重要。不建议间隔使用runtime.Goexit()作为经常的控制流机制,因为它可能使代码逻辑变得难以理解和维护。更建议使用通道(channels)和上下文包进行协作式终止使用。 panic和recover机制
Go语言的panic和recover机制类似于其他语言的异常处理。panic用于发出一个运行时错误,它会中断正常的程序流程,并沿着调用堆栈向上回溯,执行沿途的defer函数。如果panic没有被recover捕获,最终会终止整个程序。
工作原理:豆包AI编程
豆包推出的AI编程助手483查看详情panic(v interface{}):抛出一个恐慌。当panic被调用时,当前函数的执行会立即停止。所有已注册的defer函数都会按照LIFO(后进先出)的顺序执行,然后控制权会传递给调用者。这个过程会一直向上重复,直到遇到一个recover调用或者到达goroutine的也一样。recover()接口{}:捕获一个恐慌。recover函数只有在defer函数中调用时才有效。如果在一个defer函数中调用了recover,并且当前goroutine正在经历一个panic,那么recover就会捕获这个panic的值,并停止恐慌的传播,使程序恢复正常执行。如果恢复在非恐慌状态下被调用,或者不在defer函数中调用,将会返回nil。
示例代码:
立即进入“豆包AI人工智官网入口”;
立即学习“豆包AI人工智能在线问答入口”;package mainimport ( quot;fmtquot;quot;timequot;)// foo 函数发送panicfunc fooWithPanic() { fmt.Println(quot;进入 fooWithPanic()quot;) // 这里转发panic fmt.Println(quot;从准备fooWithPanic() 发送panic...quot;) panic(quot;退出协程的自定义错误quot;) // 这行代码将永远不会被执行 fmt.Println(quot;这行代码在panic之后,不会被执行。quot;)}// bar 函数调用 fooWithPanicfunc barWithPanic() { fmt.Println(quot;进入 barWithPanic()quot;) defer fmt.Println(quot;barWithPanic() 的 defer 被执行quot;) fooWithPanic() fmt.Println(quot;这行代码在 fooWithPanic() 之后,不会被执行。
quot;)}// goroutine函数是我们的主协程逻辑,包含recoverfunc myGoroutineWithRecover() { fmt.Println(quot;myGoroutineWithRecover开始运行quot;) // 使用defer和recover来捕获panic defer func() { if r :=recover(); r != nil { fmt.Printf(quot;myGoroutineWithRecover 确定到panic: v\nquot;, r) // 可以在这里进行一些清理或日志记录 } fmt.Println(quot;myGoroutineWithRecover 的 defer 被执行quot;) }() for i := 0; i lt; 5; i { fmt.Printf(quot;myGoroutineWithRecover 循环 d\nquot;, i) barWithPanic() fmt.Printf(quot;myGoroutineWithRecover 循环 d 结束\nquot;, i) // 这行代码在第一次循环后不会被执行 time.Sleep(100 * time.Millisecond) } fmt.Println(quot;myGoroutineWithRecover 正常结束quot;) // 这行代码不会被执行}func main() { fmt.Println(quot;main 协程开始quot;) go myGoroutineWithRecover() // main 让协程保持运行时间 time.Sleep(1 * time.Second) fmt.Println(quot;main 协程结束quot;)}登录后复制
输出示例:main 协程开始myGoroutineWithRecover 开始运行 myGoroutineWithRecover 循环 0进入 barWithPanic()进入 fooWithPanic()准备 fooWithPanic() 抛出panic...barWithPanic() 的 defer 被执行myGoroutineWithRecover 阻塞到panic:退出协程的自定义错误 myGoroutineWithRecover 的 defer 被执行main 协程结束登录后复制
从输出中可以看出,当fooWithPanic()中发送panic后,fooWithPanic()和barWithPanic()中panic之后的代码都不会执行。barWithPanic()的defer函数被执行。最重要的是的,myGoroutineWithRecover()中的defer函数捕获了panic,阻止了它继续向上冒泡导致程序崩溃,并且执行了myGoroutineWithRecover()本身的defer。协程在捕获panic后实际上已经终止了其循环体的执行。
panic是否需要恢复?是的,如果panic没有在当前goroutine的恐慌被恢复捕获,会导致整个程序崩溃。因此,如果你打算使用panic作为控制流来退出协程,必须在协程的入口点(或者上层调用)使用def er结合recover来捕获它,以防止程序意外终止。
注意事项:panic和recover主要用于处理真正的异常情况,例如不可恢复的编程错误或断言失败。不建议将panic和recover作为常规的控制流机制,因为这使得代码难以阅读和推送理,并且可能引入难以调试的bug。Go语言通过返回错误值来处理可预期的错误。恢复只在defer函数中调用才有效的总结。与最佳实践
在Go语言中,从协程堆栈的各个位置退出协程有两种主要方式:
runtime.Go exit():优点:直接、简洁,只终止当前协程,并保证推迟函数执行。缺点:不提供错误信息传递机制,不适用于常规控制流程。适用场景:当协程完成其任务或遇到不可继续执行的条件时,需要立即自行停止,且不影响其他协程或主程序。
panic和 优点:优点:可以提交错误信息,模拟异常处理,可以在协程入口处捕获以防止崩溃程序。缺点:设计初衷是处理异常控制流,而不是会降低代码的判断性和可维护性。必须结合恢复使用,否则会导致程序崩溃。适用场景:真正处理不可不可的运行时错误,或者在框架特定中作为一种特殊的错误机制处理(如HTTP请求处理中间件)。
推荐的最佳实践:对于需要从外部控制协程终止的情况,或者协程需要协作方式地停止时,更推荐使用context.Context或通道(channel)进行信号通知。这些方法提供了更优雅、更可控的协程管理方式,允许协程在收到停止信号后进行清理并安全退出,而不是被强制禁止。例如,通过context.WithCancel创建一个可取消的下游,将这个上下文给协程提供。协程内部定期检查context.Done()通道,一旦通道关闭,则表示收到取消信号,协程即可自行退出。这种方式是Go社区广泛推荐的协程管理模式。
选择哪种退出机制取决于具体的场景和需求。对于简单的、自发的协程终止,runtime.Goexit()可能就足够了。但如果涉及到错误处理、资源清理和复杂的控制流,或者需要更灵活的协作方式终止,则应优先考虑通道和上下文。
以上就是如何从Go协程堆栈的任意位置安全退出的详细内容,更多请关注乐哥常识网其他相关文章! 相关标签: python java go语言栈 ai 编程代码强制性 Python Java中间件 throw 循环栈 堆 raise 接口 Go语言 nil 通道 http bug 低代码大家都在看: 使用Python模拟Shell环境:实现命令面积链式执行 Pythoncurses库如何使用Python使用三角形时避免数学域错误python 文件后缀是什么 Python 删除列表填充:一种灵活的解决方案
