成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

常見的 Goroutine 泄露,你應該避免

開發 后端
盡管 Goroutines 非常方便,但如果不小心處理,它們很容易引入難以追蹤的錯誤,Goroutine 泄露就是其中之一。

Go 語言編寫代碼的最大優點之一是能夠在輕量級線程,即 Goroutines 中并發運行你的代碼。

然而,擁有強大的能力也伴隨著巨大的責任。

盡管 Goroutines 非常方便,但如果不小心處理,它們很容易引入難以追蹤的錯誤。

Goroutine 泄露就是其中之一。它在背景中悄悄增長,可能最終在你不知情的情況下使你的應用程序崩潰。

因此,本文主要介紹 Goroutine 泄露是什么,以及你如何防止泄露發生。

我們來看看吧!

什么是 Goroutine 泄露?

當創建一個新的 Goroutine 時,計算機在堆中分配內存,并在執行完成后釋放它們。

Goroutine 泄露是一種內存泄露,當 Goroutine 沒有終止并在應用程序的生命周期中被留在后臺時就會發生。

讓我們來看一個簡單的例子。

func goroutineLeak(ch chan int) {
    data := <- ch
    fmt.Println(data)
}

func handler() {
    ch := make(chan int)
    
    go goroutineLeak(ch)
    return
}

隨著處理器的返回,Goroutine 繼續在后臺活動,阻塞并等待數據通過通道發送 —— 這永遠不會發生。

因此,產生了一個 Goroutine 泄露。

在本文中,我將引導你了解兩種常見的模式,這些模式很容易導致 Goroutine 泄漏:

  • 遺忘的發送者
  • 被遺棄的接收者

讓我們深入研究!

遺忘的發送者

遺忘的發送者發生在發送者被阻塞,因為沒有接收者在通道的另一側等待接收數據的情況。

func forgottenSender(ch chan int) {
    data := 3
  
    // This is blocked as no one is receiving the data
    ch <- data
}

func handler () {
    ch := make(chan int)
  
    go forgottenSender(ch)
    return
}

雖然它起初看起來很簡單,但在以下兩種情境中很容易被忽視。

不當使用 Context

func forgottenSender(ch chan int) {
    data := networkCall()
  
    ch <- data
}

func handler() error {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
    defer cancel()
  
    ch := make(chan int)
    go forgottenSender(ch)
  
    select {
        case data := <- ch: {
            fmt.Printf("Received data! %s", data)
      
            return nil
        }
    
        case <- ctx.Done(): {
            return errors.New("Timeout! Process cancelled. Returning")
        }
    }
}

在上面的例子中,我們模擬了一個標準的網絡服務處理程序。

我們定義了一個上下文,它在10ms后發出超時,隨后是一個異步進行網絡調用的Goroutine。

select語句等待多個通道操作。它會阻塞,直到其其中一個情況可以運行并執行該情況。

如果網絡調用完成之前超時到達,case <- ctx.Done() 將會執行,處理程序將返回一個錯誤。

當處理程序返回時,不再有任何接收者等待接收數據。forgottenSender將被阻塞,等待有人接收數據,但這永遠不會發生!

這就是Goroutine泄露的地方。

錯誤檢查后的接收者位置

這是另一個典型的情況。

func forgottenSender(ch chan int) {
    data := networkCall()
  
    ch <- data
}

func handler() error {
    ch := make(chan int)
    go forgottenSender(ch)
  
    err := continueToValidateOtherData()
    if err != nil {
        return errors.New("Data is invalid! Returning.")
    }
  
    data := <- ch
  
    return nil
}

在上面的例子中,我們定義了一個處理程序并生成一個新的Goroutine來異步進行網絡調用。

在等待調用返回的過程中,我們繼續其他的驗證邏輯。

如你所見,當continueToValidateOtherData返回一個錯誤導致處理程序返回時,泄露就發生了。

沒有人等待接收數據,forgottenSender將永遠被阻塞!

解決方案:忘記的發送者

使用一個緩沖通道。

如果你回想一下,忘記的發送者發生是因為另一端沒有接收者。阻塞問題的罪魁禍首是一個無緩沖的通道!

一個無緩沖的通道是在消息發出時立即需要一個接收者的,否則發送者會被阻塞。它是在沒有為通道分配容量的情況下聲明的。

func forgottenSender(ch chan int) {
    data := 3
  
    // This will NOT block
    ch <- data
}

func handler() {
    // Declare a BUFFERED channel
    ch := make(chan int, 1)
  
    go forgottenSender(ch)
    return
}

通過為通道添加特定的容量,在這種情況下為1,我們可以減少所有提到的問題。

發送者可以在不需要接收者的情況下將數據注入通道。

被遺棄的接收者

正如其名字所暗示的,被遺棄的接收者是完全相反的情況。

當一個接收者被阻塞,因為另一邊沒有發送者發送數據時,它就會發生。

func abandonedReceiver(ch chan int) {
    // This will be blocked
    data := <- ch
  
    fmt.Println(data) 
}

func handler() {
    ch := make(chan int)
  
    go abandonedReceiver(ch)
  
    return
}

第3行一直被阻塞,因為沒有發送者發送數據。

讓我們再次了解兩個常見的場景,這些場景經常被忽視。

發送者未關閉的通道

func abandonedWorker(ch chan string) {
    for data := range ch {
        processData(data)
    }
  
    fmt.Println("Worker is done, shutting down")
}

func handler(inputData []string) {
    ch := make(chan string, len(inputData))
  
    for _, data := range inputData {
        ch <- data
    }
  
    go abandonedWorker(ch)
    
    return
}

在上面的例子中,處理程序接收一個字符串切片,創建一個通道并將數據插入到通道中。

處理程序然后通過Goroutine啟動一個工作程序。工作程序預計會處理數據,并且一旦處理完通道中的所有數據,就會終止。

然而,即使消耗并處理了所有的數據,工作程序也永遠不會到達“第6行”!

盡管通道是空的,但它沒有被關閉!工作程序繼續認為未來可能會有傳入的數據。因此,它坐下來并永遠等待。

這是Goroutine再次泄漏的地方。

在錯誤檢查之后放置發送者

這與我們之前的一些示例非常相似。

func abandonedWorker(ch chan []int) {
    data := <- ch

    fmt.Println(data)
}

func handler() error {
    ch := make(chan []int)
    go abandonedWorker(ch)

    records, err := getFromDB()
    if err != nil {
        return errors.New("Database error. Returning")
    }

    ch <- records

    return nil
}

在上面的例子中,處理程序首先啟動一個Goroutine工作程序來處理和消費一些數據。

然后,處理程序從數據庫中查詢記錄,然后將記錄注入通道供工作程序使用。

如果數據庫出現錯誤,處理程序將立即返回。通道將不再有任何發送者傳入數據。

因此,工作程序被遺棄。

解決方案:被遺棄的接收者

在這兩種情況下,接收者都被留下,因為他們“認為”通道將有傳入的數據。因此,它們阻塞并永遠等待。

解決方案是一個簡單的單行代碼。

defer close(ch)

當你啟動一個新的通道時,最好的做法是推遲關閉通道。

這確保在數據發送完成或函數退出時關閉通道。

接收者可以判斷一個通道是否已關閉,并相應地終止。

func abandonedReceiver(ch chan int) {
    // This will NOT be blocked FOREVER
    data := <- ch
  
    fmt.Println(data) 
}

func handler() {
    ch := make(chan int)
  
      // Defer the CLOSING of channel
    defer close(ch)
  
    go abandonedReceiver(ch)
    return
}

結論

關于 Goroutine 泄漏就是這么多了!

盡管它不像其他 Goroutine 錯誤那么強大,但這種泄漏仍然會大大耗盡應用程序的內存使用。

記住,擁有強大的力量也伴隨著巨大的責任。

保護我們的應用程序免受錯誤的責任在于你我——開發人員!

責任編輯:趙寧寧 來源: 技術的游戲
相關推薦

2014-10-15 10:01:12

2015-11-20 13:17:23

世紀互聯藍云Azure

2023-09-02 21:31:16

Java內存泄漏

2025-02-14 08:56:09

GoroutineContextChannel

2022-12-27 14:52:31

Kubernetes云原生開發

2021-11-02 08:41:13

黑客網絡安全網絡攻擊

2018-09-18 10:55:24

人工智能機器學習深度學習

2018-09-12 23:15:43

2014-08-13 15:55:17

Web響應式設計design

2025-01-22 07:59:59

2022-04-22 17:07:02

源代碼開源代碼泄漏

2023-12-05 08:02:51

JavaScript字符串功能

2019-07-10 08:56:50

Java技術容器

2015-09-01 16:27:31

薪資錯誤

2021-07-16 10:27:07

ITIT領導IT管理

2023-11-28 08:22:05

goroutine語言

2022-03-08 09:26:41

物聯網安全物聯網

2019-07-11 10:42:57

容器ArrayList JMH

2016-05-25 10:03:51

JavaScript內存泄露

2019-11-12 16:34:12

網絡安全軟件技術
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久久久久久久久久久91 | 91久久久www播放日本观看 | 成人夜晚看av| 午夜欧美一区二区三区在线播放 | 国产欧美精品一区二区 | 国产精品久久久久久久久图文区 | 精国产品一区二区三区四季综 | 日韩高清国产一区在线 | 天堂在线中文字幕 | 精品1区 | 国产欧美日韩久久久 | 国产精品亚洲成在人线 | 狠狠干狠狠操 | 欧美视频精品 | 最新国产视频 | 欧美激情精品久久久久久变态 | 国产精品久久久久久久久久妇女 | 亚洲国产精品网站 | 一区二区国产精品 | 亚洲一区二区三区在线观看免费 | 超级乱淫av片免费播放 | 一区二区三区在线 | 天堂亚洲 | 99久久免费精品国产男女高不卡 | 久久九九99 | 亚洲欧洲日本国产 | 中文字幕av网站 | 久久精品电影 | 999免费观看视频 | 亚洲精品乱码8久久久久久日本 | 久久久久国产一区二区三区 | 精品久久精品 | 不卡一区二区三区四区 | 日韩精品在线观看一区二区三区 | 超碰在线免费av | 国产福利91精品 | 一本久久a久久精品亚洲 | 男人天堂免费在线 | 国产精品永久免费视频 | 亚洲欧美综合精品另类天天更新 | 伦理午夜电影免费观看 |