Go 通道引用與close操作的實(shí)現(xiàn)
在 Go 開發(fā)中,通道(chan)的使用頻率極高,但它的引用特性和 close 操作的作用范圍,往往是新手容易踩坑的點(diǎn)。比如 “通道賦值后關(guān)閉原變量,新變量會(huì)受影響嗎?”“會(huì)不會(huì)導(dǎo)致內(nèi)存泄露?”“后續(xù)發(fā)送數(shù)據(jù)會(huì)不會(huì) panic?”—— 這篇文章就用通俗的語言 + 結(jié)論 + 代碼驗(yàn)證,把這些問題講透。
一、核心結(jié)論(先給答案,不繞彎)
destChan不是指針,是「通道引用」:Go 中通道是引用類型(類似 slice、map),變量存儲(chǔ)的是指向底層數(shù)據(jù)結(jié)構(gòu)的引用,而非結(jié)構(gòu)本身。- 關(guān)閉
source.TaskChan后,destChan會(huì)受影響,但不是 “被關(guān)閉”:close 操作作用于底層通道,所有引用這個(gè)通道的變量(包括destChan)都會(huì)感知到 “通道已關(guān)閉”。 - 不會(huì)因
destChan導(dǎo)致內(nèi)存泄露:只要所有引用(source.TaskChan和destChan)都不再被使用,底層通道會(huì)被 GC 回收;真正需要警惕的是 “關(guān)閉通道后的發(fā)送 panic”。
二、逐點(diǎn)拆解:把原理講明白
1. 通道是 “引用類型”,不是指針但行為類似
Go 中的引用類型(chan/slice/map/func/interface)有個(gè)共性:變量存儲(chǔ)的是「指向底層對(duì)象的地址」,賦值操作只會(huì)復(fù)制這個(gè)地址,不會(huì)復(fù)制底層對(duì)象。
舉個(gè)實(shí)際場(chǎng)景的例子:
// 假設(shè) source 是一個(gè)自定義結(jié)構(gòu)體,TaskChan 是已初始化的 chan string
type Resource struct {
TaskChan chan string // 通道字段
}
var source = Resource{
TaskChan: make(chan string, 5), // 初始化帶緩沖通道
}
// 賦值操作:將 source.TaskChan 賦值給 destChan
destChan := source.TaskChan
執(zhí)行后,destChan 和 source.TaskChan 持有同一個(gè)底層通道的引用 —— 就像兩個(gè)遙控器控制同一個(gè)電視,操作任何一個(gè),影響的都是同一個(gè) “底層設(shè)備”。
這里要注意:destChan 不是 *chan string(通道指針),而是 chan string(通道引用類型),語法上不需要解引用(*)就能直接使用,比指針更簡(jiǎn)潔。
2. close 操作作用于 “底層通道”,所有引用都會(huì)感知
當(dāng)我們執(zhí)行 close(source.TaskChan) 時(shí),要明確一個(gè)關(guān)鍵:關(guān)閉的是底層的通道對(duì)象,不是 source.TaskChan 這個(gè)變量本身。
因?yàn)?nbsp;destChan 和 source.TaskChan 指向同一個(gè)底層通道,所以 destChan 會(huì)變成 “指向已關(guān)閉通道的引用”—— 此時(shí)會(huì)有兩個(gè)核心影響:
- 往
destChan發(fā)送數(shù)據(jù):直接 panic(錯(cuò)誤信息:send on closed channel); - 從
destChan接收數(shù)據(jù):會(huì)立即返回通道元素的零值 +ok=false(表示通道已關(guān)閉且無數(shù)據(jù))。
可以用一個(gè)通俗的比喻理解:兩個(gè)指針指向同一個(gè)文件,關(guān)閉文件后,兩個(gè)指針都無法再寫入文件,但指針變量本身還存在(不是 nil),只是失去了有效操作的能力。
3. 內(nèi)存泄露風(fēng)險(xiǎn):幾乎不存在,無需過度擔(dān)心
內(nèi)存泄露的核心是 “底層對(duì)象被無用的引用持有,無法被 GC 回收”,但在這個(gè)場(chǎng)景中,完全不需要擔(dān)心:
destChan通常是局部變量(比如在函數(shù)或回調(diào)中定義),函數(shù)執(zhí)行完畢后,變量會(huì)被銷毀,引用自然釋放;source.TaskChan是結(jié)構(gòu)體字段,當(dāng)source結(jié)構(gòu)體被銷毀(比如任務(wù)執(zhí)行結(jié)束后),這個(gè)引用也會(huì)消失;- 只要所有引用都釋放,無論底層通道是否關(guān)閉,都會(huì)被 GC 回收,不會(huì)造成內(nèi)存泄露。
唯一可能的泄露場(chǎng)景:如果 source 是全局變量(長(zhǎng)期存在),且通道被關(guān)閉后,source.TaskChan 仍被持有,但這是 source 的生命周期管理問題,和 destChan 無關(guān)。
三、代碼驗(yàn)證:直觀感受引用與 close 的影響
光說不練假把式,用一段簡(jiǎn)單的代碼驗(yàn)證上面的結(jié)論,跑起來就能直觀看到效果:
package main
import "fmt"
func main() {
// 1. 初始化一個(gè)通道(底層通道對(duì)象在堆上分配)
sourceChan := make(chan string, 1)
fmt.Printf("sourceChan 變量本身地址(棧上):%p\n", &sourceChan)
fmt.Printf("sourceChan 引用的底層通道地址(堆上):%p\n", sourceChan)
// 2. 賦值給 destChan:復(fù)制引用
destChan := sourceChan
fmt.Printf("destChan 變量本身地址(棧上):%p\n", &destChan)
fmt.Printf("destChan 引用的底層通道地址(堆上):%p\n", destChan)
// 3. 關(guān)閉 sourceChan(實(shí)際關(guān)閉的是底層通道)
close(sourceChan)
// 4. destChan 感知到底層通道已關(guān)閉
data, ok := <-destChan
fmt.Printf("從 destChan 接收數(shù)據(jù):data=%q, ok=%v(ok=false 表示通道已關(guān)閉)\n", data, ok)
// 5. 往 destChan 發(fā)送數(shù)據(jù)會(huì)直接 panic(注釋掉可避免運(yùn)行報(bào)錯(cuò))
// destChan <- "test" // 執(zhí)行后報(bào)錯(cuò):panic: send on closed channel
}
運(yùn)行結(jié)果:
sourceChan 變量本身地址(棧上):0xc0000a6020
sourceChan 引用的底層通道地址(堆上):0xc0000b4000
destChan 變量本身地址(棧上):0xc0000a6040
destChan 引用的底層通道地址(堆上):0xc0000b4000
從 destChan 接收數(shù)據(jù):data="", ok=false(ok=false 表示通道已關(guān)閉)
結(jié)論驗(yàn)證:
sourceChan和destChan是不同的變量(棧地址不同),但引用同一個(gè)底層通道(堆地址相同);- 關(guān)閉
sourceChan后,destChan能直接感知到通道關(guān)閉; - 關(guān)閉后的通道發(fā)送數(shù)據(jù)會(huì) panic,這是核心風(fēng)險(xiǎn)點(diǎn)。
到此這篇關(guān)于Go 通道引用與close操作的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go 通道引用與close操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用Go+Redis實(shí)現(xiàn)分布式鎖的示例代碼
在分布式的業(yè)務(wù)中 , 如果有的共享資源需要安全的被訪問和處理 , 那就需要分布式鎖,本文主要介紹了用Go+Redis實(shí)現(xiàn)分布式鎖的示例代碼,感興趣的可以了解一下2021-12-12
golang在GRPC中設(shè)置client的超時(shí)時(shí)間
這篇文章主要介紹了golang在GRPC中設(shè)置client的超時(shí)時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04
深入解析快速排序算法的原理及其Go語言版實(shí)現(xiàn)
這篇文章主要介紹了快速排序算法的原理及其Go語言版實(shí)現(xiàn),文中對(duì)于快速算法的過程和效率有較為詳細(xì)的說明,需要的朋友可以參考下2016-04-04
Golang 獲取系統(tǒng)信息的實(shí)現(xiàn)
本文主要介紹了Golang 獲取系統(tǒng)信息的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
輕松構(gòu)建Go應(yīng)用的Dockerfile
本文介紹了如何制作一個(gè)用于構(gòu)建和運(yùn)行Go應(yīng)用程序的Docker鏡像的Dockerfile的相關(guān)資料,需要的朋友可以參考下2023-10-10
golang執(zhí)行命令獲取執(zhí)行結(jié)果狀態(tài)(推薦)
這篇文章主要介紹了golang執(zhí)行命令獲取執(zhí)行結(jié)果狀態(tài)的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2019-11-11
GO語言標(biāo)準(zhǔn)錯(cuò)誤處理機(jī)制error用法實(shí)例
這篇文章主要介紹了GO語言標(biāo)準(zhǔn)錯(cuò)誤處理機(jī)制error用法,實(shí)例分析了錯(cuò)誤處理機(jī)制的具體用法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12

