golang 并發

所有工具的簡單介紹

1 goroutine:
準確來說這并不是一個和C#Java一樣的線程,而是golang runtime管理的一個輕量級線程,但是我們完全可以把他當做是一個線程,使用go關鍵字來開啟
2 channel:
這是一種通信方式,相當于不同線程之間建立了管道通信的機制,管道有很多種,但是只要把握住核心功能“通信”,就不會感到迷茫,很巧妙的解決了共享內存的線程的諸多問題
3 select:
這相當于是對channel進行集中處理的工具,就像戰爭片里的指揮所里一群人在一起使用電報進行集中收發,當一個線程需要受到很多信息 或者說管道的影響的時候,必然需要對這些信息進行集中管理
4 sync包:
提供了一些工具,分別有各種鎖 mutex,waitGroup,once,atomic……,這幾個是主要的,mutex是解決并發運行的時候數據錯亂的方式,類似于數據庫遇到的各種幻讀,臟讀等情況,有了鎖就能實現一些原子化操作,waitGroup相當于是一個計數器,wg.wait會一直等待,直到wg.Done調用的次數多余wg.Add(n) 的時候才會停止阻塞繼續運行,once也和數量有關,一個once只能執行一次操作,atomic是一些已經寫好了的原子化操作,比如a=a+1并不是原子化的,但是atomic.AddInt64(&a,1)就是原子化的,context可以理解為一種特殊的管道操作,不過專門負責控制取消線程。
可以看到這個包是對線程的具體內容進行控制
5 time包中的Ticker:
我認為這個也很重要,在一些涉及到時間的并發場景中這個會十分方便,比如輪詢之類的操作。

goroutine

完全可以把他當做線程對待,使用go進行調用,一般使用兩種姿勢
go functionName(arguments)
或者
go func() {
// 函數體
}()

channel

最普通的channel創建:
var ch chan strign
ch=make(chan string)
銷毀:
close(ch)
使用:
ch<-msg
msg:=<-ch
//這里有個注意,所有的使用方式都只能是<-向左,不像C++方向可以變<< 或者 >>
不管是發送還是接受信息的時候,這種最簡單的管道在沒有聯系完成的時候會一直堵塞住,簡單的來說,如果ch<-"hhha",會一直堵塞到有線程接收了這個hhha不然發送線程就會一直堵塞,同理,在接收線程的msg<-ch語句沒有接收到信息的時候,也會一直堵塞住。而這種堵塞會一直持續,一直到什么時候聯系完成或者通道關閉,關閉的時候,會直接給msg賦0值,比如string
就會是“”。

這是最基礎的channel,為什么設計成這樣,這是谷歌多年來無數代碼總結的經驗,這種方式犧牲了一部分并發的效率,但是出錯的概率很小,雖然不夠靈活,但是基本的功能其實已經完成了,golang完成異步編程的辦法和C#等不一樣,go接受了異步代碼和同步不一樣的事實,選擇讓開發者使用channel自己控制邏輯,我認為更靈活,并且,在最基礎的channel上還加上一些升級功能,如下。

升級一:
雙值接受,當通道關閉之后還沒有接收到想要的內容,這往往意味著大事不妙,肯定要進行特殊處理并進行修復。這時候使用msg,ok:=<-ch,如果是因為ch關閉而停止的阻塞,那么ok會為false,開發者最好能在對可能出現提前關閉的channel進行處理時使用ok來報錯提示

升級二:
這里引入buffered channel和unbuffered channel 的概念,最基礎的channel就是unbuffered channel,我們可以在定義的時候make(chan int, 9)來創建buffered channel。
加入了緩沖區的channel的發送者在緩沖區爆滿之前是不會堵塞的,而接受者還是會在接收不到的時候堵塞,其實為換句話說,sender被阻塞的時候,其實是沒有發送成功的,只有被另一端讀走一個數據之后才算是send成功,unbuffered其實也算是一種buffered,只是buffer為1

高級裝逼用法,nil類型和channel類型的channel,一般用處不大,但是也存在這種用法