Go 知识点(11) — goroutine 泄露、设置子协程退出条件
生活随笔
收集整理的這篇文章主要介紹了
Go 知识点(11) — goroutine 泄露、设置子协程退出条件
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1. 問題現象
如果在開發過程中不考慮 goroutine 在什么時候能退出和控制 goroutine 生命期,就會造成 goroutine 失控或者泄露的情況 ,看示例代碼:
func consumer(ch chan int) {for {data := <-chfmt.Println(data)}
}func main() {ch := make(chan int)for {var input string// 獲取輸入,模擬進程持續運行fmt.Scan(&input)go consumer(ch)// 輸出現在的goroutine數量fmt.Println("goroutines 個數為:", runtime.NumGoroutine())}}
運行程序,每輸入一個字符串+回車,將會創建一個 goroutine, 結果如下 :
a
goroutines 個數為: 2
b
goroutines 個數為: 3
c
goroutines 個數為: 4
d
goroutines 個數為: 5
e
goroutines 個數為: 6
f
goroutines 個數為: 7
g
goroutines 個數為: 8
h
goroutines 個數為: 9
i
goroutines 個數為: 10
上面代碼模擬一個進程根據需要創建 goroutine 的情況 。我們發現, 隨著輸入的字符串越來越多, goroutine 將會無限制地被創建,但并不會結束,因為 consumer 是個阻塞操作,且 channel 中沒有讓其退出的操作。如果一直持續下去將會造成內存大量分配,最終使進程崩潰。
那要如何解決呢?
2. 解決方案
2.1 創建單個子協程,在子協程中處理業務
修改上面代碼,將創建子協程的代碼挪動到循環外面。
func consumer(ch chan int) {for {data := <-chfmt.Println("data is: ", data)}
}func main() {ch := make(chan int)go consumer(ch) // 在循環外面創建協程for {var input stringfmt.Scan(&input)fmt.Println("goroutines 個數為:", runtime.NumGoroutine())}}
這樣輸出結果為:
a
goroutines 個數為: 2
v
goroutines 個數為: 2
x
goroutines 個數為: 2
w
goroutines 個數為: 2
f
goroutines 個數為: 2
g
goroutines 個數為: 2
b
goroutines 個數為: 2
從結果可以看到協程數量并沒有隨著收入字符的增多而增加,但是存在一個問題就是,子協程并沒有退出的機制。
如何解決呢?接著往下看
2.2 設置子協程退出條件
在主協程中設置當輸入字符串為 quit 時,往通道里面寫入 -1,子協程從通道里面獲取數據為 -1 時就退出。此時主協程仍然是有效的,但是子協程會永遠退出,所以協程數量為 1 。
func consumer(ch chan int) {for {data := <-ch// 收到的數據為 -1 時,退出該循環,同時也會退出該協程if data == -1 {break}fmt.Println("data is: ", data)}}func main() {ch := make(chan int)go consumer(ch) // 在循環外面創建協程for {var input stringfmt.Scan(&input)if input == "quit" {ch <- -1 // 當輸入為 quit 時,往通道里面寫入 -1}// 輸出現在的goroutine數量fmt.Println("goroutines 個數為:", runtime.NumGoroutine())}
}
輸出結果:
a
goroutines 個數為: 2
b
goroutines 個數為: 2
c
goroutines 個數為: 2
d
goroutines 個數為: 2
quit
goroutines 個數為: 2
a
goroutines 個數為: 1
b
goroutines 個數為: 1
3. 總結
從上面示例我們可以總結使用協程的一般原則:
- 盡量避免無限制的創建協程;
- 在需要反復創建協程的場景下,協程一定要有退出的條件,并且確保該退出條件能滿足(即代碼能執行到);
總結
以上是生活随笔為你收集整理的Go 知识点(11) — goroutine 泄露、设置子协程退出条件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022-2028年中国气相防锈薄膜行业
- 下一篇: Go 知识点(12) — 类型转换以三方