go chan基本使用詳解
1、有緩沖的chan 與無緩沖的chan
怎么理解這個緩沖,我個人的理解是是執(zhí)行這個chan 操作的時候是否發(fā)送阻塞。
操作:讀和寫。
讀取的時候,我們都應(yīng)該要是阻塞的,例如我們的socket、的recv函數(shù)。當(dāng)然取決于你設(shè)置的是阻塞的套接字還是非阻塞的套接字了。
無緩沖的chan,講究讀寫對稱,也就是你在讀的時候會阻塞,看下面這個例子:ch是一個無緩沖的chan,在主線程里面,ch<-發(fā)送了阻塞。所以后面沒法執(zhí)行了。
func TestChan1(t *testing.T) { ch := make(chan int) <-ch ch<-1 }
我們對此進(jìn)行修改,同樣的也是無緩沖的chan,只不過讓讀操作異步,也就是不是阻塞在主線程了,讓主線程可以繼續(xù)執(zhí)行。
func TestChan1(t *testing.T) { ch := make(chan int) go func() { val := <-ch fmt.Println(val) }() ch<-1 time.Sleep(1*time.Second) }
以上是無緩沖chan 的讀操作,假設(shè)我們是先寫呢?我們應(yīng)該可以猜想到,寫可能發(fā)生阻塞也可能不發(fā)生阻塞。那么無緩沖的chan 到底會不會阻塞呢?我們看下面的例子
func TestChan1(t *testing.T) { ch := make(chan int) ch <- 1 <-ch }
運行之后,發(fā)生了死鎖。
對此我們可以得出的初步結(jié)論是:無緩沖的chan 讀寫都是阻塞的。
同理我們對此進(jìn)行修改
func TestChan1(t *testing.T) { ch := make(chan int) go func() { ch <- 1 }() val := <-ch fmt.Println(val) }
無緩沖的chan的介紹,到以上就結(jié)束,我們看一下有緩沖的chan。
2、有緩沖的chan
猜想一下有緩沖的chan 是什么存在緩沖,也就是說是讀寫操作哪個是非阻塞的,還是都是非阻塞的。我們看下面的例子。
第一個例子
func TestChan1(t *testing.T) { ch := make(chan int,1) ch <- 1 val := <-ch fmt.Println(val) }
先寫入ch 然后讀取。運行
這里我們得到結(jié)論:寫是非阻塞。
第2 個例子:
func TestChan1(t *testing.T) { ch := make(chan int,1) <-ch ch <- 1 }
很顯然我們可以猜到,會死鎖。運行
對此我們對于有緩沖的chan得出的結(jié)論:讀取是阻塞的。
同理,針對上面的修改:
func TestChan1(t *testing.T) { ch := make(chan int,1) go func() { val := <-ch fmt.Println(val) }() ch <- 1 time.Sleep(1 *time.Second) }
對此我們對于chan有了一個基本的認(rèn)識與使用。接下來看一下chan 幾個應(yīng)用實例。
3、利用chan 實現(xiàn)生產(chǎn)者消費者
生產(chǎn)者與消費者,說白了就是一個線程負(fù)責(zé)產(chǎn)生數(shù)據(jù),另外一端消費數(shù)據(jù)。對應(yīng)于我們的讀寫操作上來,生產(chǎn)者寫數(shù)據(jù),消費者讀數(shù)據(jù)。對于該模型是不是,很容易利用chan來實現(xiàn)呢?假設(shè)我們現(xiàn)在是1個生產(chǎn)者,1個消費者,那么我們應(yīng)該利用幾個chan呢,很顯然是一個chan 就夠了,因為寫入需要阻塞,那么我們的produce 是需要一個線程的,對于消費者,我們也需要一個線程,具體實現(xiàn):
func TestChan1(t *testing.T) { ch := make(chan int,1) defer close(ch) go func() { for i := 0;i<10;i++ { ch<-i fmt.Println("send:",i) } }() go func() { for { select { case val, ok := <-ch: if ok { fmt.Println("recv:", val) } else { return } } } }() /*go func() { for c := range ch { fmt.Println(c) fmt.Println("recv:",c) } }()*/ time.Sleep(1 *time.Second) }
針對接收數(shù)據(jù),我們通常采用以下這種模式。
for { select { case <- ch: case <-ctx.Down: .... } }
4、利用chan 實現(xiàn)同步
兩條線程交替打印,例如:1-100,兩條線程交替打印。
分析一下這個操作,時間上我們利用的是chan的讀取阻塞的特性,實際上就是利用chan 實現(xiàn)同步。
func TestChan1(t *testing.T) { ch1 := make(chan int) ch2 := make(chan int) go func() { for i := 0; i < 50; i++ { <-ch1 fmt.Println(2*i + 1) ch2 <- 1 } }() go func() { for i := 0; i < 50; i++ { <-ch2 fmt.Println(2*i + 2) ch1 <- 1 } }() ch1 <- 1 time.Sleep(1 * time.Second) }
5、并發(fā)處理
假設(shè)我們有一個任務(wù),這個任務(wù)可以分成很多份,每個任務(wù)處理的都是相同的內(nèi)容,例如多線程查詢,匯總。多線程上傳。具體的chan 模板代碼:
// eg1: 假設(shè)10條線程處理,采用10個chan的方式 var res = 0 func TestChan() { ch := make(chan int,1) closeCh := make(chan int,1) defer close(ch) for i := 1;i<=10;i++ { item := i go func() { ch <- item }() } go func() { for i := 0;i<10;i++{ c := <- ch res += c //fmt.Println(val) } closeCh<-1 }() <-closeCh fmt.Println(res) }
運行結(jié)果
使用waitgroup
func WgTest() { ch := make(chan int, 1) closeCh := make(chan int,1) wg := sync.WaitGroup{} wg.Add(2) go Produce(ch,&wg) go Produce(ch,&wg) go Merge(ch,closeCh) wg.Wait() close(ch) <-closeCh fmt.Println(result) return } func Produce(ch chan int, wg *sync.WaitGroup) { defer func() { wg.Done() }() for i := 0; i < 10; i++ { ch <- i } return } var result = 0 func Merge(ch,closeCh chan int) { for { select { case val,ok := <-ch: if ok { result += val }else { closeCh<-1 return } } } }
到此這篇關(guān)于go chan基本使用詳解的文章就介紹到這了,更多相關(guān)go chan使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang?中?channel?的詳細(xì)使用、使用注意事項及死鎖問題解析
這篇文章主要介紹了golang?中?channel?的詳細(xì)使用、使用注意事項及死鎖分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03Golang 經(jīng)典校驗庫 validator 用法解析
這篇文章主要為大家介紹了Golang 經(jīng)典校驗庫 validator 用法解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08CSP communicating sequential processes并發(fā)模型
這篇文章主要為大家介紹了CSP communicating sequential processes并發(fā)模型,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05GoLang bytes.Buffer基礎(chǔ)使用方法詳解
Go標(biāo)準(zhǔn)庫中的bytes.Buffer(下文用Buffer表示)類似于一個FIFO的隊列,它是一個流式字節(jié)緩沖區(qū),我們可以持續(xù)向Buffer尾部寫入數(shù)據(jù),從Buffer頭部讀取數(shù)據(jù)。當(dāng)Buffer內(nèi)部空間不足以滿足寫入數(shù)據(jù)的大小時,會自動擴(kuò)容2023-03-03一站式解決方案:在Windows和Linux上快速搭建Go語言開發(fā)環(huán)境
本文將介紹如何在Windows和Linux操作系統(tǒng)下搭建Go語言開發(fā)環(huán)境,以幫助您更高效地進(jìn)行Go語言開發(fā),需要的朋友可以參考下2023-10-10