詳解go語(yǔ)言判斷管道是否關(guān)閉的常見(jiàn)誤區(qū)
前言
本文是探討的是"在Go語(yǔ)言中,我們是否可以使用讀取管道時(shí)的第二個(gè)返回值來(lái)判斷管道是否關(guān)閉?"
樣例
在Go語(yǔ)言中,我們是否可以使用讀取管道時(shí)的第二個(gè)返回值來(lái)判斷管道是否關(guān)閉? 可以看下面的代碼
package main import "fmt" func main() { // 創(chuàng)建一個(gè)整型管道 ch := make(chan int) // 啟動(dòng)一個(gè)協(xié)程往管道發(fā)送數(shù)據(jù) go func() { for i := 0; i < 5; i++ { ch <- i } // 關(guān)閉管道 close(ch) }() // 能否判斷管道是否關(guān)閉? if _, ok := <-ch; !ok { fmt.Println("管道已關(guān)閉") } }
探討 管道的數(shù)據(jù)結(jié)構(gòu)
在探討這個(gè)問(wèn)題之前,我們先來(lái)了解一下管道的數(shù)據(jù)結(jié)構(gòu),從go的源碼,我們可以知道,管道是被定義為一個(gè)名為hchan的結(jié)構(gòu)體:
type hchan struct { qcount uint //當(dāng)前隊(duì)列中剩余的元素個(gè)數(shù) dataqsiz uint // 環(huán)形隊(duì)列管道容積 buf unsafe.Pointer // 環(huán)形隊(duì)列指針 elemsize uint16 // 元素大小 closed uint32 // 標(biāo)識(shí)管道關(guān)閉的狀態(tài) elemtype *_type // 元素類(lèi)型 recvq waitq // 等待的讀元素的協(xié)程隊(duì)列 sendq waitq // 等待的寫(xiě)元素的協(xié)程隊(duì)列 ... }
其中,有一個(gè)屬性是我們應(yīng)該關(guān)注的,那就是closed,這玩意標(biāo)識(shí)了管道是否關(guān)閉,這玩意為1代表關(guān)閉了,為0代表是開(kāi)啟的.
詳細(xì)分析
好的,接下來(lái)我們繼續(xù)本文探討的問(wèn)題在Go語(yǔ)言中,我們是否可以使用管道的第二個(gè)返回值來(lái)判斷管道是否關(guān)閉?先給出結(jié)論 : 從嚴(yán)格意義上來(lái)講是不可以的,其實(shí)表示是否成功讀取數(shù)據(jù),但是在緩存區(qū)為0的時(shí)候,ok的狀態(tài)和管道狀態(tài)是一致的,所以會(huì)被誤認(rèn)為,這個(gè)ok是代表管道的狀態(tài)可以看下面的例子
package main import ( "fmt" "time" ) func main() { a2 := make(chan int, 2) go demo(a2) value2, ok2 := <-a2 fmt.Printf("value2:%v,ok2:%v\n", value2, ok2) time.Sleep(3 * time.Second) value3, ok3 := <-a2 fmt.Printf("value3:%v,ok3:%v\n", value3, ok3) } func demo(a chan int) { defer func() { close(a) fmt.Println("管道已經(jīng)關(guān)閉") }() a <- 1 a <- 2 }
解釋一下運(yùn)行流程
1.首先創(chuàng)建了一個(gè)緩存區(qū)為2的管道a2
2.然后用go關(guān)鍵字
將demo函數(shù)開(kāi)辟出一個(gè)新的協(xié)程運(yùn)行,此時(shí)demo和main是同一級(jí)的關(guān)系,同時(shí)運(yùn)行,此時(shí)main函數(shù)會(huì)繼續(xù)向下執(zhí)行,發(fā)現(xiàn)是從管道中讀取一個(gè)元素,然后就會(huì)等待demo函數(shù)會(huì)向管道中傳入值,demo函數(shù)的運(yùn)行過(guò)程是這樣的,它發(fā)現(xiàn)管道a2的緩存是2,所以剛好把元素存入,然后就執(zhí)行關(guān)閉管道,然后demo協(xié)程銷(xiāo)毀
3.main函數(shù)繼續(xù)執(zhí)行,接收到a2管道的一個(gè)元素之后,然后返回value2和ok2,然后進(jìn)行打印
4.然后休眠3秒鐘
5.然后繼續(xù)讀取a2管道的元素,得到value3和ok3,然后打印
ok2和ok3都為true
’ 管道已經(jīng)關(guān)閉 ’ 這是最先打印的,無(wú)論運(yùn)行多少次,都是一樣的,而且我還特地將main函數(shù)暫停了3秒,所以我可以保證demo函數(shù)已經(jīng)執(zhí)行完畢,demo協(xié)程已經(jīng)銷(xiāo)毀,然后再執(zhí)行的第二個(gè)管道的數(shù)據(jù)的讀取
逐步調(diào)試
那我們調(diào)試一下,可以發(fā)現(xiàn),執(zhí)行了make函數(shù)創(chuàng)建管道之后,管道沒(méi)有關(guān)閉,我前面特意提了管道的數(shù)據(jù)結(jié)構(gòu),其中closed是標(biāo)識(shí)管道是否關(guān)閉的
繼續(xù)調(diào)試,我們可以發(fā)現(xiàn),在讀取完管道a2的第一個(gè)值賦值給value2和ok2的時(shí)候,此時(shí)通道已經(jīng)關(guān)閉
value2的值為1,ok2為true
繼續(xù)調(diào)試,通道還是關(guān)閉狀態(tài),但是ok3的值還是true,看下面的第二張圖
所以讀取管道元素傳來(lái)的第二個(gè)值,并不是代表管道是否關(guān)閉!
那它代表什么?
其實(shí)是代表讀取數(shù)據(jù)是否成功,或者說(shuō)代表緩存區(qū)是否還有數(shù)據(jù)
首先我們要知道, 關(guān)閉了的管道, 我們還是可以進(jìn)行讀取的, 這個(gè)設(shè)定是因?yàn)橛芯彺娴拇嬖? 但是如果管道關(guān)閉了的話,又沒(méi)有值,讀取的話,會(huì)是類(lèi)型的默認(rèn)值和false,也就是讀取未成功
當(dāng)然如果是緩存區(qū)為0的情況,ok的值和管道的狀態(tài)是一致的
var c = make(chan int) close(c) value, ok := <-c fmt.Printf("value:%v \nok:%v \n", value, ok)
運(yùn)行結(jié)果:
以上就是詳解go語(yǔ)言判斷管道是否關(guān)閉的常見(jiàn)誤區(qū)的詳細(xì)內(nèi)容,更多關(guān)于go判斷管道是否關(guān)閉的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang使用sqlite3數(shù)據(jù)庫(kù)實(shí)現(xiàn)CURD操作
這篇文章主要為大家詳細(xì)介紹了Golang使用sqlite3數(shù)據(jù)庫(kù)實(shí)現(xiàn)CURD操作的相關(guān)知識(shí),文中的示例代碼簡(jiǎn)潔易懂,有需要的小伙伴可以參考一下2025-03-03Golang學(xué)習(xí)筆記之安裝Go1.15版本(win/linux/macos/docker安裝)
這篇文章主要介紹了Golang學(xué)習(xí)筆記之安裝Go1.15版本(win/linux/macos/docker安裝),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12輕松構(gòu)建Go應(yīng)用的Dockerfile
本文介紹了如何制作一個(gè)用于構(gòu)建和運(yùn)行Go應(yīng)用程序的Docker鏡像的Dockerfile的相關(guān)資料,需要的朋友可以參考下2023-10-10實(shí)現(xiàn)像php一樣方便的go ORM數(shù)據(jù)庫(kù)操作示例詳解
這篇文章主要為大家介紹了實(shí)現(xiàn)像php一樣方便的go ORM數(shù)據(jù)庫(kù)操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12使用Go語(yǔ)言開(kāi)發(fā)短鏈接服務(wù)的方法
短鏈接一般是通過(guò)映射關(guān)系,將長(zhǎng)長(zhǎng)的一串網(wǎng)址,映射到幾個(gè)字符的短鏈接上,建立好這種映射關(guān)系之后保存到數(shù)據(jù)庫(kù)里,用戶每次訪問(wèn)短鏈接的時(shí)候,這篇文章主要介紹了使用Go語(yǔ)言開(kāi)發(fā)一個(gè)短鏈接服務(wù),需要的朋友可以參考下2024-03-03