淺析Go使用定時(shí)器時(shí)如何避免潛在的內(nèi)存泄漏陷阱
引出問(wèn)題
先看一個(gè)例子,我們?cè)?Go 中的 select 使用定時(shí)器,實(shí)現(xiàn)為消息監(jiān)聽(tīng)加上超時(shí)能力。
核心代碼,如下所示:
func main() { ch := make(chan int) // 啟動(dòng)一個(gè)goroutine go func() { for { select { case num := <-ch: fmt.Println("獲取到的數(shù)字是", num) case <-time.After(2 * time.Second): fmt.Println("時(shí)間到了!!!") } } }() for i := 0; i < 5; i++ { ch <- i time.Sleep(1 * time.Second) } }
在這個(gè)例子中,select 語(yǔ)句用于監(jiān)聽(tīng) channel 消息和超時(shí)。然而,我要關(guān)注的重點(diǎn)是 timer 的行為。它是不是能達(dá)到我們預(yù)期的目標(biāo)呢?為消息監(jiān)聽(tīng)加上超時(shí)效果呢?
檢查定時(shí)器行為
如果運(yùn)行這段代碼,將會(huì)發(fā)現(xiàn),如果 timer 設(shè)置為 2 秒,主循環(huán)設(shè)置 1 秒的延遲時(shí)間,timer 不會(huì)觸發(fā)。
如下是程序的運(yùn)行輸出:
獲取到的數(shù)字是 0
獲取到的數(shù)字是 1
獲取到的數(shù)字是 2
獲取到的數(shù)字是 3
獲取到的數(shù)字是 4
這是因?yàn)槊看窝h(huán),time.After 創(chuàng)建都會(huì)返回一個(gè)新的定時(shí)器,產(chǎn)生的后果就是,每次多會(huì)重置 select 調(diào)用的時(shí)間。
相反,如果將定時(shí)器的超時(shí)設(shè)置為 1 秒,將主循環(huán)的time.Sleep設(shè)置為 2 秒,就能觸發(fā)定時(shí)器,輸出 "時(shí)間到了!!!"。這證明了這個(gè)定時(shí)器是有效運(yùn)行的。
潛在的內(nèi)存泄漏
Go標(biāo)準(zhǔn)庫(kù)文檔提到,每次調(diào)用time.After都會(huì)創(chuàng)建一個(gè)新的定時(shí)器。然而,我們需要認(rèn)真考慮一個(gè)重要問(wèn)題。
來(lái)自官方文檔引用:
The underlying Timer is not recovered by the garbage collector until the timer fires.
如果這些 timer 沒(méi)有達(dá)到設(shè)定時(shí)間,就不會(huì)被 GC。這會(huì)導(dǎo)致內(nèi)存泄漏。毫無(wú)疑問(wèn),如果在常駐程序中頻繁使用 timer 的,內(nèi)存泄漏將會(huì)日積月累。
最佳實(shí)踐
要高效地管理資源并避免 timer 的內(nèi)存泄漏,建議使用 time.NewTimer 和 timer.Reset 組合。這種方法允許重復(fù)使用一個(gè)定時(shí)器,減少資源消耗和潛在的內(nèi)存泄漏風(fēng)險(xiǎn)。
例如,如下是使用 time.NewTimer 改進(jìn)的代碼示例:
// 為定時(shí)器定義持續(xù)時(shí)間。 idleDuration := 5 * time.Minute // 使用指定的持續(xù)時(shí)間創(chuàng)建新的定時(shí)器。 idleDelay := time.NewTimer(idleDuration) // 確保定時(shí)器適當(dāng)?shù)赝V挂员苊赓Y源泄漏。 defer idleDelay.Stop() // 進(jìn)入循環(huán)以處理傳入的消息或基于時(shí)間的事件。 for { // 在每次循環(huán)迭代開(kāi)始時(shí)重置定時(shí)器到指定的持續(xù)時(shí)間。 idleDelay.Reset(idleDuration) // 使用select等待多個(gè)通道操作。 select { // 處理傳入消息的情況。 case s, ok := <-in: // 檢查通道是否關(guān)閉。如果是,退出循環(huán)。 if !ok { return } // 處理接收到的消息`s`。 // 在這里添加相關(guān)代碼來(lái)處理消息。 // 處理定時(shí)器超時(shí)的情況。 case <-idleDelay.C: // 增加空閑計(jì)數(shù)器或處理超時(shí)事件。 // 這通常是您會(huì)在這里添加代碼來(lái)處理超時(shí)情況的地方。 idleCounter.Inc() // 處理取消或上下文過(guò)期的情況。 case <-ctx.Done(): // 如果上下文已完成,則退出循環(huán)。 return } }
流程如下所示:
這里例子中演示了 Go 語(yǔ)言中如何正確使用和管理 timer。通過(guò)遵循 Go 標(biāo)準(zhǔn)庫(kù)的建議將能產(chǎn)出更高效和可靠的程序。
結(jié)論
本文通過(guò)一個(gè)代碼案例演示了 GO 中 timer.After
可能產(chǎn)生的潛在內(nèi)存泄漏問(wèn)題。通過(guò)使用官方推薦的方案,利用重置定時(shí)器時(shí)間實(shí)現(xiàn) Timer
的重復(fù)利用,避免了潛在的內(nèi)存泄漏問(wèn)題。
到此這篇關(guān)于淺析Go使用定時(shí)器時(shí)如何避免潛在的內(nèi)存泄漏陷阱的文章就介紹到這了,更多相關(guān)Go內(nèi)存泄漏內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go文件操作(新建打開(kāi)寫入讀取刪除關(guān)閉)學(xué)習(xí)筆記
這篇文章主要為大家介紹了Go文件操作(新建打開(kāi)寫入讀取刪除關(guān)閉)學(xué)習(xí)筆記,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01golang 中strings包的Replace的使用說(shuō)明
這篇文章主要介紹了golang 中strings包的Replace的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03golang接收post和get請(qǐng)求參數(shù)處理
本文主要介紹了golang接收post和get請(qǐng)求參數(shù)處理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03關(guān)于Golang獲取當(dāng)前項(xiàng)目絕對(duì)路徑的問(wèn)題
這篇文章主要介紹了Golang獲取當(dāng)前項(xiàng)目絕對(duì)路徑的問(wèn)題,通常的做法是go run用于本地開(kāi)發(fā),用一個(gè)命令中快速測(cè)試代碼確實(shí)非常方便;在部署生產(chǎn)環(huán)境時(shí),我們會(huì)通過(guò)go build構(gòu)建出二進(jìn)制文件然后上傳到服務(wù)器再去執(zhí)行,那么會(huì)產(chǎn)生什么問(wèn)題呢?感興趣的朋友一起看看吧2022-04-04golang語(yǔ)言http協(xié)議get拼接參數(shù)操作
這篇文章主要介紹了golang語(yǔ)言http協(xié)議get拼接參數(shù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12一文帶你了解Golang中reflect反射的常見(jiàn)錯(cuò)誤
go?反射的錯(cuò)誤大多數(shù)都來(lái)自于調(diào)用了一個(gè)不適合當(dāng)前類型的方法,?而且,這些錯(cuò)誤通常是在運(yùn)行時(shí)才會(huì)暴露出來(lái),而不是在編譯時(shí),如果我們傳遞的類型在反射代碼中沒(méi)有被覆蓋到那么很容易就會(huì)?panic。本文就介紹一下使用?go?反射時(shí)很大概率會(huì)出現(xiàn)的錯(cuò)誤,需要的可以參考一下2023-01-01