golang channel讀取數(shù)據(jù)的幾種情況
用var定義channel且不make
wg := sync.WaitGroup{} var ch chan string read := func() { ?? ?fmt.Println("reading") ?? ?s := <-ch ?? ?fmt.Println("read:", s) ?? ?wg.Done() } write := func() { ?? ?fmt.Println("writing") ?? ?s := "t" ?? ?ch <- s ?? ?fmt.Println("write:", s) ?? ?wg.Done() } wg.Add(2) go read() go write() fmt.Println("waiting") wg.Wait()
輸出:
waiting
writing
reading
fatal error: all goroutines are asleep - deadlock!
這種情況并不是報(bào)錯(cuò)空指針,而是死鎖。加上make看看
用var定義channel且make
wg := sync.WaitGroup{} var ch = make(chan string) read := func() { ?? ?fmt.Println("reading") ?? ?s := <-ch ?? ?fmt.Println("read:", s) ?? ?wg.Done() } write := func() { ?? ?fmt.Println("writing") ?? ?s := "t" ?? ?ch <- s ?? ?fmt.Println("write:", s) ?? ?wg.Done() } wg.Add(2) go read() go write()
輸出
waiting
writing
reading
read: t
write: t
這種情況沒(méi)什么毛病,之所以先輸出的read,是因?yàn)镮O機(jī)制。下面給寫加上for
直給寫操作加for
wg := sync.WaitGroup{} var ch = make(chan string) read := func() { ?? ?fmt.Println("reading") ?? ?s := <-ch ?? ?fmt.Println("read:", s) ?? ?wg.Done() } write := func() { ?? ?for { ?? ??? ?fmt.Println("writing") ?? ??? ?s := "t" ?? ??? ?ch <- s ?? ??? ?fmt.Println("write:", s) ?? ?} ?? ?wg.Done() } wg.Add(2) go read() go write() fmt.Println("waiting") wg.Wait() fmt.Println("finish")
輸出
waiting
reading
writing
write: t
writing
read: t
fatal error: all goroutines are asleep - deadlock!
報(bào)錯(cuò)說(shuō)所有的協(xié)程都睡著,意思就是runtime發(fā)現(xiàn)沒(méi)有能拿來(lái)調(diào)度的協(xié)程了,報(bào)錯(cuò)退出。如果是在大項(xiàng)目中,這里則會(huì)阻塞,runtime會(huì)調(diào)度其他可運(yùn)行的協(xié)程。下面把for移到讀操作上。
直給讀操作加for
wg := sync.WaitGroup{} var ch = make(chan string) read := func() { ?? ?for { ?? ??? ?fmt.Println("reading") ?? ??? ?s := <-ch ?? ??? ?fmt.Println("read:", s) ?? ?} ?? ?wg.Done() } write := func() { ?? ?fmt.Println("writing") ?? ?s := "t" ?? ?ch <- s ?? ?fmt.Println("write:", s) ?? ?wg.Done() } wg.Add(2) go read() go write() fmt.Println("waiting") wg.Wait() fmt.Println("finish")
輸出
waiting
reading
writing
write: t
read: t
reading
fatal error: all goroutines are asleep - deadlock!
跟上面現(xiàn)象基本一樣,不再贅述,然后給倆操作都加上for
讀寫都加for
wg := sync.WaitGroup{} var ch = make(chan string) read := func() { ?? ?for { ?? ??? ?fmt.Println("reading") ?? ??? ?s := <-ch ?? ??? ?fmt.Println("read:", s) ?? ?} ?? ?wg.Done() } write := func() { ?? ?for { ?? ??? ?fmt.Println("writing") ?? ??? ?s := "t" ?? ??? ?ch <- s ?? ??? ?fmt.Println("write:", s) ?? ?} ?? ?wg.Done() } wg.Add(2) go read() go write() fmt.Println("waiting") wg.Wait() fmt.Println("finish")
輸出
waiting
writing
reading
read: t
write: t
writing
reading
read: t
reading
write: t
writing
write: t
writing
...
結(jié)果當(dāng)然就是死循環(huán)了,這個(gè)很好理解。接下來(lái)才是本文的重點(diǎn):讀數(shù)據(jù)的第二個(gè)參數(shù)。我們先保持其他的都不動(dòng),在讀的時(shí)候接收第二個(gè)返回值。
讀channel的第二個(gè)返回值
wg := sync.WaitGroup{} var ch = make(chan string) read := func() { ?? ?for { ?? ??? ?fmt.Println("reading") ?? ??? ?s, ok := <-ch ?? ??? ?fmt.Println("read:", s, ok) ?? ?} ?? ?wg.Done() } write := func() { ?? ?for { ?? ??? ?fmt.Println("writing") ?? ??? ?s := "t" ?? ??? ?ch <- s ?? ??? ?fmt.Println("write:", s) ?? ?} ?? ?wg.Done() } wg.Add(2) go read() go write() fmt.Println("waiting") wg.Wait() fmt.Println("finish")
輸出
waiting
writing
reading
read: t true
reading
write: t
writing
write: t
writing
read: t true
reading
read: t true
reading
write: t
...
可以看出來(lái),這第二個(gè)返回值是個(gè)bool類型,目前全都是true。那么什么時(shí)候會(huì)是false呢,把channel關(guān)上試試。為了更直觀,把字符串的長(zhǎng)度一起輸出
關(guān)閉channel繼續(xù)讀
wg := sync.WaitGroup{} var ch = make(chan string) read := func() { ?? ?for { ?? ??? ?fmt.Println("reading") ?? ??? ?s, ok := <-ch ?? ??? ?fmt.Println("read:", len(s), s, ok) ?? ?} ?? ?wg.Done() } write := func() { ?? ?for i := 0; i < 5; i++ { ?? ??? ?fmt.Println("writing") ?? ??? ?s := "t" ?? ??? ?ch <- s ?? ??? ?fmt.Println("write:", s) ?? ?} ?? ?wg.Done() ?? ?close(ch) } wg.Add(2) go read() go write() fmt.Println("waiting") wg.Wait() fmt.Println("finish")
輸出
waiting
writing
reading
read: 1 t true
reading
write: t
writing
write: t
writing
read: 1 t true
reading
read: 1 t true
reading
write: t
writing
write: t
writing
read: 1 t true
reading
read: 1 t true
reading
write: t
read: 0 false
reading
read: 0 false
reading
read: 0 false
...
接下來(lái)就是很規(guī)律的死循環(huán)了。這樣是不是可以猜測(cè),從已經(jīng)close的channle讀數(shù)據(jù),會(huì)讀到該數(shù)據(jù)類型的零值,且第二個(gè)返回值為false?再試試給channel加個(gè)buffer,先寫完關(guān)上再開始讀
寫完然后關(guān)閉channel再開始讀
wg := sync.WaitGroup{} var ch = make(chan string, 5) read := func() { ?? ?for { ?? ??? ?fmt.Println("reading") ?? ??? ?s, ok := <-ch ?? ??? ?fmt.Println("read:", len(s), s, ok) ?? ?} ?? ?wg.Done() } write := func() { ?? ?for i := 0; i < 5; i++ { ?? ??? ?fmt.Println("writing") ?? ??? ?s := "t" ?? ??? ?ch <- s ?? ??? ?fmt.Println("write:", s) ?? ?} ?? ?wg.Done() ?? ?close(ch) ?? ?fmt.Println("closed") } wg.Add(2) write() go read() fmt.Println("waiting") wg.Wait() fmt.Println("finish")
輸出
writing
write: t
writing
write: t
writing
write: t
writing
write: t
writing
write: t
closed
waiting
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 0 false
reading
read: 0 false
reading
read: 0 false
...
我們把寫操作前的go關(guān)鍵字去了,并且在關(guān)閉channel之后加了log??梢院芮逦目吹?,先往channel里寫了5次,然后close了,之后才有wait及read的log。并且前5個(gè)ok是true,后面循環(huán)輸出false?,F(xiàn)在我們可以得出結(jié)論當(dāng)channel關(guān)閉且數(shù)據(jù)都讀完了,再讀數(shù)據(jù)會(huì)讀到該數(shù)據(jù)類型的零值,且第二個(gè)返回值為false。下面再套上select
加個(gè)select
wg := sync.WaitGroup{} var ch = make(chan string, 5) read := func() { ?? ?for { ?? ??? ?fmt.Println("reading") ?? ??? ?select { ?? ??? ?case s, ok := <-ch: ?? ??? ??? ?fmt.Println("read:", len(s), s, ok) ?? ??? ?} ?? ?} ?? ?wg.Done() } write := func() { ?? ?for i := 0; i < 5; i++ { ?? ??? ?fmt.Println("writing") ?? ??? ?s := "t" ?? ??? ?ch <- s ?? ??? ?fmt.Println("write:", s) ?? ?} ?? ?wg.Done() ?? ?close(ch) ?? ?fmt.Println("closed") } wg.Add(2) write() go read() fmt.Println("waiting") wg.Wait() fmt.Println("finish")
輸出
writing
write: t
writing
write: t
writing
write: t
writing
write: t
writing
write: t
closed
waiting
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 0 false
reading
read: 0 false
reading
read: 0 false
...
很明顯跟上面現(xiàn)象一致,如果忘了關(guān)閉channel呢?
channel未及時(shí)關(guān)閉
wg := sync.WaitGroup{} var ch = make(chan string, 5) read := func() { ?? ?for { ?? ??? ?fmt.Println("reading") ?? ??? ?select { ?? ??? ?case s, ok := <-ch: ?? ??? ??? ?fmt.Println("read:", len(s), s, ok) ?? ??? ?} ?? ?} ?? ?wg.Done() } write := func() { ?? ?for i := 0; i < 5; i++ { ?? ??? ?fmt.Println("writing") ?? ??? ?s := "t" ?? ??? ?ch <- s ?? ??? ?fmt.Println("write:", s) ?? ?} ?? ?wg.Done() ?? ?//close(ch) ?? ?//fmt.Println("closed") } wg.Add(2) write() go read() fmt.Println("waiting") wg.Wait() fmt.Println("finish")
輸出
writing
write: t
writing
write: t
writing
write: t
writing
write: t
writing
write: t
waiting
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
fatal error: all goroutines are asleep - deadlock!
睡著了,然后報(bào)錯(cuò)。跟上面情況一樣,如果是在大項(xiàng)目中,runtime會(huì)調(diào)度其他可運(yùn)行的協(xié)程。最后來(lái)總結(jié)一下怎么操作才算優(yōu)(sao)雅(qi)。
總結(jié)
- 對(duì)寫的一方來(lái)說(shuō),一定記著及時(shí)關(guān)閉channel,避免出現(xiàn)協(xié)程泄露。雖然它占得資源少,省點(diǎn)電不香么。
- 對(duì)讀的一方來(lái)說(shuō),除非十分確定數(shù)據(jù)的個(gè)數(shù),最好是用for來(lái)讀數(shù)據(jù),省的在“管兒”里有“野數(shù)據(jù)”造成內(nèi)存泄露。同時(shí)根據(jù)第二個(gè)返回值的真假來(lái)控制for循環(huán),避免出現(xiàn)“無(wú)效工作量”
到此這篇關(guān)于golang channel讀取數(shù)據(jù)的幾種情況的文章就介紹到這了,更多相關(guān)golang channel讀取內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang NewRequest/gorequest實(shí)現(xiàn)http請(qǐng)求的示例代碼
本文主要介紹了golang NewRequest/gorequest實(shí)現(xiàn)http請(qǐng)求的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08Go語(yǔ)言error的設(shè)計(jì)理念及背景演化詳解
這篇文章主要為大家介紹了Go語(yǔ)言error的設(shè)計(jì)理念及背景演化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12go?zero微服務(wù)實(shí)戰(zhàn)處理每秒上萬(wàn)次的下單請(qǐng)求
這篇文章主要為大家介紹了go?zero微服務(wù)實(shí)戰(zhàn)處理每秒上萬(wàn)次的下單請(qǐng)求示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07Go語(yǔ)言開發(fā)k8s之ConfigMap操作解析
這篇文章主要為大家介紹了Go語(yǔ)言開發(fā)k8s之ConfigMap操作示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06