1、go協程是什麼
go協程是與其他函式或方法一起併發執行的函式或方法。go協程可以看作是輕量級執行緒。與執行緒相比
建立一個go協程的成本很小
2、go協程相比執行緒的優勢
a:相比執行緒而言,go協程的成本極低。堆疊大小隻有若干kb,可以根據應用的需求進行增減。
而執行緒必須指定堆疊的大小,其堆疊固定不變
b:go協程會複用數量更少的os執行緒。即使程式有數以千計的go協程,也可能只有一個執行緒。
如果go協程發生來阻塞,系統會再建立一個os執行緒,並把其餘go協程都移到這個新的OS執行緒
c:go協程使用通道來進行通訊。通道用於防止多個協程訪問共享記憶體時發生競態條件,通道可以
看作是go協程之間通訊的管道
3、啟動go協程
呼叫函式或者方法時,在前面加上關鍵字go,可以讓一個新的go協程併發的執行
1、啟動一個新的協程時,協程的呼叫會立即返回。與函式不同,程式控制不會去等待go協程執行完畢。
在呼叫go協程之後,程式控制會立即返回到程式碼的下一行,忽略該協程的任何返回值
2、如果希望執行其他的go協程,go主執行緒必須繼續執行著。如果go主協程終止,則程式終止,於是其他
go協程不會繼續執行
3、在go主執行緒中使用休眠,以便等待其他執行緒執行完畢,只是用於理解go協程如何工作的技巧。
通道可用於在其他協程結束執行之前阻塞go主協程
func main(){
go hello()
time.Sleep(1 * time.Second)
fmt.Println("xsxs")
}
4、啟動多個go協程複製程式碼
通道可以想象成go協程之間通訊的管道
1、通道的宣告
所有通道都關聯來一個型別。通道只能運輸這種型別的資料,而運輸其他型別的資料是非法的
chan T表示T型別的通道
定義通道: a := make(chan int)
2、通過通道進行傳送和接收
data := <- a //讀取通道a
a <- data //寫入通道a
3、傳送與接收預設是阻塞的
a:當資料傳送到通道時,程式控制會在傳送資料的語句處發生阻塞,直到有其它go協程從通道讀取資料,
才會解除阻塞
b:當讀取通道的資料時,如果沒有其它的協程把資料寫入到通道,那麼讀取過程就會一直阻塞
幫助go協程之間高效的通訊
4、死鎖
a:go協程給一個通道傳送資料時,照理說會有其它go協程來接收資料,如果沒有程式就會在執行時觸
發panic,形成死鎖
b:同理,接收時也一樣
5、單向通道
只能傳送或者接收資料
a:通道轉換:把一個雙向通道轉換程唯送通道或者唯收通道
6、關閉通道和使用for range遍歷通道
a:資料傳送方可以關閉通道,通知接收方這個通道不再有資料傳送過來
b:從通道接收資料時,接收方可以多用一個變數來檢查通道是否已經關閉
v, ok := <- ch複製程式碼
1、緩衝通道
a:無緩衝通道的傳送和接收過程都是阻塞的
b:有緩衝的通道:
一:只在緩衝已滿的情況,才會阻塞向緩衝通道傳送資料
二:只有在緩衝為空的時候,才會阻塞從緩衝通道接收資料
c:建立緩衝通道
ch := make(chan type, capacity) capacity表示容量,無緩衝通道預設是0
2、死鎖
func main(){
ch := make(chan string, 2)
ch <- "nav"
ch <- "pal"
ch <- "ste"
fmt.Println(<-ch)
fmt.Println(<-ch)
}
向容量為2 的緩衝通道寫入3個字串,程式在第三次寫入時,由於超出來通道的容量,因此這次寫入發生
了阻塞。
想要這次寫操作進行下去,必須要有其他協程來讀取這個通道的資料,所有這裡發生死鎖
3、長度和容量
a:緩衝通道的容量是指通道可以儲存的值的數量 make函式建立時指定
b:長度是指當前排隊的元素個數
4、waitGroup
a:waitGroup用於實現工作池,用於等待一批go協程執行結束。程式控制會一直阻塞,直到這些協程全部
執行完畢
b:傳遞wg的地址是很重要的。如果沒有傳遞wg的地址,那麼每個go協程將會得到一個waitGroup值的拷貝,
因而當它們執行結束時,main函式並不會知道
go process(i, &wg)
5、工作池的實現
a:緩衝通道的重要作用之一就是實現工作池。工作池就是一組等待任務分配的執行緒
b:工作池的核心功能:
一:建立一個go協程池,監聽一個等待作業分配的輸入型緩衝通道
二:將作業新增到該輸入型緩衝通道
三:作業完成後,再將結果寫入一個輸出型緩衝通道
四:從輸出型緩衝通道讀取並列印結果
c:隨著工作協程數量的增加,完成作業的總時間會減少複製程式碼
1、select語句用於在多個傳送/接收通道操作中進行選擇。select語句會一直阻塞,直到傳送/接收操作準備
就緒。
如果有多個通道操作準備完畢,select會隨機選取其中之一執行
2、select應用
3、預設情況
在沒有case準備就緒時,可以執行select語句中的預設情況,防止select語句一直阻塞
4、死鎖與預設情況
func main(){
ch := make(chan string)
select {
case <- ch:
}
}
a:如果存在預設情況,就不會發生死鎖
b:如果select只包含值為nil的值,也同樣會執行預設情況
5、隨機選取
當select由多個case準備就緒時,將會隨機選取其中之一去執行
在playground上執行不具有隨機性
6、空select
select語句沒有任何case,會一直阻塞,導致死鎖複製程式碼
1、臨界區
當程式併發執行時,多個go協程不應該同時訪問那些修改共享資源的程式碼
2、mutex用於提供一種加鎖機制,可確保某時刻只有一個協程在臨界區執行,防止出現競態條件
A:mutex定義了兩個方法:Lock和Unlock。所有在Lock和Unlock之間的程式碼,都只能由一個go協程執行,
可以避免競態條件
3、使用mutex
傳遞mutex的地址很重要,如果傳遞的是mutex的值而非地址,那麼每個協程都會得到mutex的一份拷貝,
競態還是會發生
4、使用通道處理競態條件
5、mutex vS 通道
當go協程需要與其他協程通訊時,可以使用通道
而當只允許一個協程訪問臨界區時,可以使用mutex複製程式碼
go有型別和方法,支援物件導向的程式設計風格,卻沒有型別的層次結構
1、go不支援類,而是提供類結構體
a:結構體中可以新增方法,這樣可以將資料和運算元據的方法繫結在一起,實現與類相似的效果
2、使用New()函式,而非構造器
a:java中使用構造器來解決,一個合法的物件必須使用引數化的構造器來建立
b:go不支援構造器,如果某型別的零值不可用,需要程式設計師來隱藏該型別,避免從其他包直接訪問
提供一種名為NewT(parameters)的函式,按照要求來初始化T型別的變數
c:go不支援類,但是結構體能很好的取代類,以New簽名的方法可以替代構造器複製程式碼
go不支援繼承,但它支援組合
1、通過巢狀結構體進行組合
a:在go中,通過在結構體巢狀結構體,可以實現組合
b:一旦一個結構體內巢狀了一個結構體欄位,go可以使我們訪問其巢狀的欄位。
2、結構體切片的巢狀
結構頭切片不能巢狀一個匿名切片複製程式碼
go通過介面實現多型,在go中隱式的實現介面。一個型別如果定義了介面所宣告的全部方法,它就實現了該介面
1、使用介面實現多型
a:一個型別如果定義了介面的所有方法,那它就隱式的實現了該介面
b:所有實現了介面的型別,都可以把它的值儲存在一個介面型別的變數中。使用介面的這種特性實現多型複製程式碼
1、defer的用途:含有defer語句的函式,會在該函式將要返回直接,呼叫另一個函式
2、延遲方法
3、實參取值
在go語言中,當執行defer語句的時候,就會對延遲函式的實參進行求值
4、defer棧
當一個函式多次呼叫defer時,go會把defer呼叫放到一個棧中,按後進先出的順序執行
5、defer的實際應用
當一個函式應該在與當前程式碼流無關的環境下呼叫時,可以使用defer複製程式碼
1、按照go的慣例,在處理錯誤時,通常都是將返回的錯誤與nil比較。nil表示沒有發生錯誤
2、從錯誤獲取更多資訊的不同方法
a:斷言底層結構體型別,使用結構體欄位獲取更多資訊 PathError
b:斷言底層結構體型別,呼叫方法獲取更多資訊 DNSError
c:直接比較
3、不可忽略的錯誤複製程式碼