go基礎之Goroutines和Channels

2022-07-11 10:15:10 字數 3642 閱讀 6513

在go語言中,每乙個併發的執行單元叫作乙個goroutine。

當乙個程式啟動時,其主函式即在乙個單獨的goroutine中執行,我們叫它main goroutine。新 的goroutine會用go語句來建立。在語法上,go語句是乙個普通的函式或方法呼叫前加上關鍵 字go。go語句會使其語句中的函式在乙個新建立的goroutine中執行。而go語句本身會迅速地 完成。

f()

go f()

除了從主函 數退出或者直接終止程式之外,沒有其它的程式設計方法能夠讓乙個goroutine來打斷另乙個的執 行,但是之後可以看到一種方式來實現這個目的,通過goroutine之間的通訊來讓乙個 goroutine請求其它的goroutine,並被請求的goroutine自行結束執行。

func main()

func spinner(delay time.duration)

}}func fib(x int) int

return fib(x-1) + fib(x-2)

}

如果說goroutine是go語音程式的併發體的話,那麼channels它們之間的通訊機制。乙個 channels是乙個通訊機制,它可以讓乙個goroutine通過它給另乙個goroutine傳送值資訊。每 個channel都有乙個特殊的型別,也就是channels可傳送資料的型別。

ch := make(chan int)
和map類似,channel也乙個對應make建立的底層資料結構的引用。當我們複製乙個channel 或用於函式引數傳遞時,我們只是拷貝了乙個channel引用,因此呼叫者何被呼叫者將引用同 乙個channel物件。和其它的引用型別一樣,channel的零值也是nil。

兩個相同型別的channel可以使用==運算子比較。如果兩個channel引用的是相通的物件,那 麼比較的結果為真。乙個channel也可以和nil進行比較。

乙個channel有傳送和接受兩個主要操作,都是用->運算子。在傳送語句中,<-運算子分割channel和要傳送的值。在接收語句中,<-運 算符寫在channel物件之前。乙個不使用接收結果的接收操作也是合法的。

channel還支援close操作,用於關閉channel,隨後對基於該channel的任何傳送操作都將導 致panic異常。對乙個已經被close過的channel之行接收操作依然可以接受到之前已經成功發 送的資料;如果channel中已經沒有資料的話講產生乙個零值的資料。

使用內建的close函式就可以關閉乙個channel:

close(ch)
乙個基於無快取channels的傳送操作將導致傳送者goroutine阻塞,直到另乙個goroutine在相 同的channels上執行接收操作,當傳送的值通過channels成功傳輸之後,兩個goroutine可以繼續執行後面的語句。反之,如果接收操作先發生,那麼接收者goroutine也將阻塞,直到有另乙個goroutine在相同的channels上執行傳送操作。

func main() 

}()// 接收者

go func()

}()// 在主goroutine列印結果

for

}

上面的例子只能傳送不限的序列,如果我們要傳送有限的序列可以通過關閉channels來實現。

當乙個channel被關閉後,再向該channel傳送資料將導致panic異常。當乙個被關閉的channel 中已經傳送的資料都被成功接收後,後續的接收操作將不再阻塞,它們會立即返回乙個零值。關閉上面例子中的naturals變數對應的channel並不能終止迴圈,它依然會收到乙個永無 休止的零值序列,然後將它們傳送給列印者goroutine。

沒有辦法直接測試乙個channel是否被關閉,但是接收操作有乙個變體形式:它多接收乙個結 果,多接收的第二個結果是乙個布林值ok,ture表示成功從channels接收到值,false表示 channels已經被關閉並且裡面沒有值可接收。

// 接收者

go func()

close(naturals)

}()go func()

close(squares)

}()for x:=range squares

}

並不用關閉每一channel。當需要告訴接收者goroutine,所有的資料傳送完畢才需要關閉channel。不管乙個channel是否被關閉,當他沒有被引用時將會被go的垃圾自動**器**。

重複關閉和關閉乙個nil值的channel會導致panic異常。

上面的**我們根據函式式程式設計可以寫成

func main() 

func counter(out chan int)

close(out)

}func squarer(out in chan int)

close(out)

}func printer(in chan int)

}

但是,在squarer函式中,我們無法保證out用來傳送資料,in用來接收資料。

為了防止這種情況,go語言型別系統提供了單方向的channel型別,分別用於只能傳送或接收資料。

型別chan<- int表示乙個只傳送int的channel,只能傳送不能接收。相反,型別<-chan int表示乙個只接收int的channel,只能接收不能傳送。

func main() 

func counter(out chan<- int)

close(out)

}func squarer(out chan<- int, in <)

close(out)

}func printer(in chan int)

}

呼叫counter(naturals)將導致將 chan int 型別的naturals隱式地轉換為 chan<- int 型別只發 送型的channel。

不支援反向轉向。

帶快取的channel內部持有乙個元素佇列。佇列的最大容量是在呼叫make函式建立channel時 通過第二個引數指定的。

ch = make(chan string, 3) //宣告了乙個容量為3的channel
向快取channel的傳送操作就是向內部快取佇列的尾部插入元素,接收操作則是從佇列的頭部 刪除元素。如果內部快取佇列是滿的,那麼傳送操作將阻塞直到因另乙個goroutine執行接收 操作而釋放了新的佇列空間。相反,如果channel是空的,接收操作將阻塞直到有另乙個 goroutine執行傳送操作而向佇列插入元素。

可以通過cap函式獲取channel內部快取的容量。len函式返回 channel內部快取佇列中有效元素的個數。

ch<-"a"

ch<-"b"

cap(ch) // "3"

len(ch) // "2"

Go基礎之 介面

在go語言中,乙個類只要實現了介面要求的所有函式,我們就說這個類實現了該介面 inte ce型別可以定義一組方法,用來表示乙個物件的行為特徵,inte ce不能包含任何變數,介面是引用型別。舉個簡單的例子,乙個動物的介面,動物有吃的能力,有叫的能力,等等,這裡省略,假如動物就只有吃和叫的能力。pac...

GO基礎之陣列

一 陣列的宣告與遍歷 package main import fmt 宣告陣列的形式1 var arr 3 int var arr2 4 int func main fmt.println a b int 遍歷陣列方式1 for i 0 i fmt.println 遍歷陣列方式2 for value...

Go 基礎之指標

區別於c c 中的指標,go語言中的指標不能進行偏移和運算,是安全指標。任何程式資料載入記憶體後,在記憶體都有他們的位址,這就是指標。而為了儲存乙個資料在記憶體中的位址,我們就需要指標變數。比如,永遠不要高估自己 這句話是我的座右銘,我想把它寫入程式中,程式一啟動這句話是要載入到記憶體 假設記憶體位...