這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
以前從未接觸過並發編程,所以,且寫且學吧
引入channel
package mainimport ( "fmt" "sync" "runtime" )var counter int = 0func Count(lock *sync.Mutex){ lock.Lock() counter++ fmt.Println(counter) lock.Unlock()}func main(){ lock := &sync.Mutex{} for i:=0;i<100;i++{ go Count(lock) } for{ lock.Lock() c:=counter lock.Unlock() runtime.Gosched() if c>=100{ break } }}
將counter從0加至10
但是上面的做法太過複雜,一直對lock進行引用,所以引用channel
package mainimport "fmt"func Count(ch chan int){ ch <- 1 fmt.Println("Counting")}func main(){ chs := make([]chan int, 10) for i:=0;i<10;i++{ chs[i]=make(chan int) go Count(chs[i]) } for _,ch := range(chs){ <-ch } fmt.Println(chs)}
簡單的解釋是,我們通過ch <- 1語句向對應的channel中寫入一個資料。在這個channel被讀取前,這個操作是阻塞的。
在所有的goroutine啟動完成後,我們通過<-ch語句從10個channel中依次讀取資料。在對應的channel寫入資料前,這個操作也是阻塞的。
從而使用這個操作替代了鎖的使用。
下面對channel進行詳細介紹:
1.基本文法:
聲明一個channel
var ch chan intvar m map[string] chan bool
定義一個channel
s := make(chan int)
在channel的用法中,最常見的包括寫入和讀出。將一個資料寫入(發送)至channel的文法很直觀,如下:
ch <- value
向channel寫入資料通常會導致程式阻塞,直到有其他goroutine從這個channel中讀取資料。從channel中讀取資料的文法是
value := <-ch
如果channel之前沒有寫入資料,那麼從channel中讀取資料也會導致程式阻塞,直到channel中被寫入資料為止。
2.select:
select 用於監控一系列檔案檔案控制代碼,一旦其中一個檔案控制代碼發生了IO操作,該select()調用就會被返回
//這裡注意,一定要定義channel之後才能使用ch := make(chan int, 1)for {select {case ch <- 0:case ch <- 1:}i := <-chfmt.Println("Value received:", i)}
3.緩衝機制:
c := make(chan int, 1024)
這樣子即使沒有讀取方,可以一直向channel中寫入,直至緩衝區被填滿
可以使用range關鍵來實現更為簡便的迴圈讀取:
這裡不知道為什麼總是會出錯!!!!
for i := range c{fmt.Println(i)}
總體程式
c := make(chan int, 10) for i:=0;i<10;i++{ c<-12 } fmt.Println(len(c)) for j := range c{ fmt.Println(j) }
4.逾時機制:
在並發編程的通訊過程中,最需要處理的就是逾時問題,即向channel寫資料時發現channel已滿,
或者從channel試圖讀取資料時發現channel為空白。
如果不正確處理這些情況,很可能會導致整個goroutine鎖死.即deadlock發生!
i:= <-ch
這個語句在ch中有值寫入時,沒有問題,一旦ch中為空白,沒有值寫入,會出現死結。
故而,逾時是一個非常實際的方法,在Go語言中,主要是借用select語句進行類比逾時
timeout := make(chan bool,1)ch :=make(chan int)go func(){time.sleep(1)timeout<-true}()select{case <-ch:case <-timeout:}
5.單向channel:
單向channel變數的聲明
var ch1 chan int// ch1是一個正常的channel,不是單向的var ch2 chan<- float64// ch2是單向channel,只用於寫float64資料var ch3 <-chan int// ch3是單向channel,只用於讀取int資料
單向channel的初始化
ch4 := make(chan int)ch5 := <-chan int(ch4) // ch5就是一個單向的讀取channelch6 := chan<- int(ch4) // ch6 是一個單向的寫入channel
使用方法
func Parse(ch <-chan int) {for value := range ch {fmt.Println("Parsing value", value)}}
6.關閉channel
關閉channel非常簡單,直接使用Go語言內建的close()函數即可:
close(ch)
判斷一個channel是否已經被關閉,可以在讀取的時候使用多重傳回值的方式:
x, ok := <-ch
第二個bool傳回值是false則表示ch已經被關閉