以下文章来源于Golang技术分享 ,作者机器铃砍菜刀
文章插图
goroutine的退出机制
Go中,goroutine是否结束执行(退出)是由其自身决定,其他goroutine只能通过消息传递的方式通知其关闭,而并不能在外部强制结束一个正在执行的goroutine 。当然有一种特殊情况会导致正在运行的goroutine会因为其他goroutine的结束而终止,即main函数退出 。
在Go中常见的控制goroutine退出方式有以下几种
// main函数的结束
func G1() { time.Sleep(time.Second) fmt.Println("G1 exit")}func main() { go G1() fmt.Println("main exit")}$ go run main.gomain exit
【goroutine的退出与泄露:如何检测和预防】如上所示,程序未等G1执行完毕,即随着main函数的退出而停止执行 。// context通知退出
func G1(ctx context.Context) { num := 0 for { select { case <-ctx.Done(): fmt.Println("G1 exit") return case <-time.After(time.Second): num++ fmt.Printf("G1 wait times: %dn", num) } }}func main() { ctx, cancel := context.WithCancel(context.Background()) go G1(ctx) time.Sleep(3*time.Second) cancel() time.Sleep(time.Second) fmt.Println("main exit")}$ go run main.goG1 wait times: 1G1 wait times: 2G1 exitmain exit
// panic异常结束func G1() { defer func() { if err := recover(); err != nil { fmt.Printf("G1 exit by panic: %vn", err) } }() _, err := os.Open("notExistFile.txt") if err != nil { panic(err) } fmt.Println("G1 exit naturally")}func main() { go G1() time.Sleep(time.Second) fmt.Println("main exit")}$ go run main.goG1 exit by panic: open notExistFile.txt: no such file or directorymain exit
上面函数G1中defer函数使用了recover来捕获panic,当panic发生时可使goroutine拿回控制权,确保程序不会将panic传递到goroutine调用栈顶部后引起崩溃 。// 执行完毕后退出
func G1() { for i := 0; i < 10000; i++ { //do some work } fmt.Println("G1 exit")}func main() { go G1() time.Sleep(time.Second) fmt.Println("main exit")}$ go run main.goG1 exitmain exit
goroutine里的任务执行完毕,即结束 。什么是goroutine泄露
如果你启动了一个goroutine,但并没有按照预期的一样退出,直到程序结束,此goroutine才结束,这种情况就是 goroutine 泄露 。当 goroutine 泄露发生时,该 goroutine 的栈一直被占用而不能释放,goroutine 里的函数在堆上申请的空间也不能被垃圾回收器回收 。这样,在程序运行期间,内存占用持续升高,可用内存越来也少,最终将导致系统崩溃 。
大多数情况下,引起goroutine泄露的原因有两类:channel阻塞;goroutine陷入死循环 。
// channel阻塞
1. 从channel里读,但是没有写
func G1() { c := make(chan int) go func() { <-c }() time.Sleep(2*time.Second) fmt.Println("G1 exit")}func main() { go G1() c := time.Tick(time.Second) for range c { fmt.Printf("goroutine [nums]: %dn", runtime.NumGoroutine()) }}$ go run main.gogoroutine [nums]: 3goroutine [nums]: 3G1 exitgoroutine [nums]: 2goroutine [nums]: 2...
2. 向已满的channel里写,但是没有读func G2(size int) { c := make(chan int, size) go func() { <-c }() go func() { for i := 0; i < 10; i++ { c <- i } }() fmt.Println("G2 exit")}var size = flag.Int("c",0, "define channel size")func main() { flag.Parse() go G2(*size) c := time.Tick(time.Second) for range c { fmt.Printf("goroutine [nums]: %dn", runtime.NumGoroutine()) }}$ go run main.go -c 2G2 exitgoroutine [nums]: 2goroutine [nums]: 2...$ go run main.go -c 11G2 exitgoroutine [nums]: 1goroutine [nums]: 1...
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 《王者荣耀》连胜封号规则是怎样的?
- Spring Boot+Socket实现与html页面的长连接
- 暹罗猫价格多少钱一只?
- 早安语录正能量的句子有哪些?
- Linux 原来是这么管理内存的
- 小雪天晴一冬的天气会怎么样
- 中国十大名茶最新排名是怎样的?
- java多线程编程的核心——AQS独占模式原理解析
- 互动场景下的低延迟编码技术
- 你的 Mac 用对了吗?推荐一些 Mac 上比较好用的软件