一、golang之OOP(orient object programming)
在函數聲明時, 在其名字之前放上一個變量, 即是一個方法。 這個附加的參數會將該函數附加到這種類型上, 即相當於為這種類型定義了一個獨占的方法。(c++中稱成員函數)
二、Goroutine和Channels
在golang中,每一個並發的執行單元叫做一個goroutine;而channel則是它們之間的通信機制,它可以讓一個goroutine通過channel給另一個goroutine發送消息;需要注意的是,每個channel都有一個特殊的類型;
ch := make(chan int)
close(ch) //關閉channel
2.1 無buffer channel
ch := make(chan int) or ch := make(chan int , 0)
一個基於無緩存Channels的發送操作將導致發送者goroutine阻塞, 直到另一個goroutine在相同的Channels上執行接收操作, 當發送的值通過Channels成功傳輸之后, 兩個goroutine可以繼續執行后面的語句。 反之, 如果接收操作先發生, 那么接收者goroutine也將阻塞, 直
到有另一個goroutine在相同的Channels上執行發送操作。
基於無緩存Channels的發送和接收操作將導致兩個goroutine做一次同步操作。 因為這個原因, 無緩存Channels有時候也被稱為同步Channels。 當通過一個無緩存Channels發送數據時, 接收者收到數據發生在喚醒發送者goroutine之前;
2.2 單方向的channel
類型 chan<- int 表示一個只發送int的channel, 只能發送不能接收。 相反, 類型 <-chan int 表示一個只接收int的channel, 只能接收不能發送。 ( 箭頭 <- 和關鍵字chan的相對位置表明了channel的方向。 ) 這種限制將在編譯期檢測。
1 12 // out 只發 channel 2 13 // in 只收channel 3 14 func squares(out chan<- int, in <-chan int) { 4 15 for v := range in { 5 16 out <- v * v 6 17 } 7 18 close(out) 8 19 }
2.3 帶buffer的channel
ch = make(chan string, 3)
向緩存Channel的發送操作就是向內部緩存隊列的尾部插入元素, 接收操作則是從隊列的頭部刪除元素。 如果內部緩存隊列是滿的, 那么發送操作將阻塞直到因另一個goroutine執行接收操作而釋放了新的隊列空間。 相反, 如果channel是空的, 接收操作將阻塞直到有另一個
goroutine執行發送操作而向隊列插入元素。
獲取channel的容量:cap(ch) 獲取channel數據的長度:len(ch)
三、select多路復用
1 package main 2 3 import ( 4 "fmt" 5 //"time" 6 ) 7 8 /* 9 select { 10 case <-ch1: 11 // ... 12 case <-ch2: 13 // ... 14 case <-ch3: 15 // ... 16 default: 17 // ... 18 } 19 */ 20 21 func main() { 22 j := make(chan int, 1) 23 //tick := time.Tick(1 * time.Second) 24 for down := 10; down > 0; down-- { 25 fmt.Println("down to here") 26 select { 27 case x := <-j: 28 fmt.Println(x) 29 case j <- down: 30 fmt.Println("put to j") 31 } 32 } 33 }
一、並發
一般情況下,我們沒法知道分別位於兩個goroutine事件x和y的執行順序,x是在y之前還是之后還是同時發生是沒法判斷的。 當我們能夠沒有辦法明確地確認一個事件是在另一個事件的前面或者后面發生的話, 就說明x和y這兩個事件是並發的。
如何避免競爭?
由於其它的goroutine不能夠直接訪問變量, 它們只能使用一個channel來發送給指定的goroutine請求來查詢更新變量。 這也就是Go的口頭禪“不要使用共享數據來通信;使用通信來共享數據”。 一個提供對一個指定的變量通過cahnnel來請求
的goroutine叫做這個變量的監控(monitor)goroutine。
二、互斥
互斥原型實現:
1 var ( 2 sema = make(chan struct{}, 1) 3 balance int 4 ) 5 6 func Add(amount int) { 7 sema <- struct{}{} 8 balance = balance + amount 9 <-sema 10 } 11 12 func Get() int { 13 sema <- struct{}{} 14 b := balance 15 <-sema 16 } 17 18 import "sync" 19 var ( 20 mu sync.Mutex 21 balance int 22 ) 23 func Add(amount int){ 24 mu.Lock() 25 balance = balance + amount 26 mu.Unlock() 27 } 28 29 func Get() int { 30 mu.Lock() 31 b := balance 32 mu.Unlock() 33 }
func Get() int {
mu.Lock()
defer mu.Unlock()
b := balance
return b
}
2.1 讀寫鎖
var rwmu sync.RWMutex ----用於多度少寫
rwmu.Rlock() / rwmu.RUnlock() ----讀鎖定,此時只能進行讀操作
rwmu.Lock() / rwmu.Unlock() ----讀寫鎖定
2.2 sync.Once初始化
1 /* 2 sync.Once。 概念上來講, 3 一次性的初始化需要一個互斥量mutex和一個boolean變量來記錄初始化是不是已經完成了; 4 互斥量用來保護boolean變量和客戶端數據結構。 Do這個唯一的方法需要接收初始化函數作為 5 其參數。 6 */ 7 var loadIconsOnce sync.Once 8 var icons map[string]int 9 func loadIcons() { 10 icons["first"] = 1 11 icons["second"] = 2 12 icons["thrid"] = 3 13 } 14 15 func Icon(name string) int { 16 loadIconsOnce.Do(loadIcons) 17 return icons[name] 18 }
2.3 sync.WaitGroup
先說說WaitGroup的用途:它能夠一直等到所有的goroutine執行完成,並且阻塞主線程的執行,直到所有的goroutine執行完成。這里要注意一下,他們的執行結果是沒有順序的,調度器不能保證多個 goroutine 執行次序,且進程退出時不會等待它們結束。
WaitGroup總共有三個方法:Add(delta int),Done(),Wait()。簡單的說一下這三個方法的作用。
Add:添加或者減少等待goroutine的數量
Done:相當於Add(-1)
Wait:執行阻塞,直到所有的WaitGroup數量變成0
如:
golang中的同步是通過sync.WaitGroup來實現的.WaitGroup的功能:它實現了一個類似隊列的結構,可以一直向隊列中添加任務,當任務完成后便從隊列中刪除,如果隊列中的任務沒有完全完成,可以通過Wait()函數來出發阻塞,防止程序繼續進行,直到所有的隊列任務都完成為止.
WaitGroup的特點是Wait()可以用來阻塞直到隊列中的所有任務都完成時才解除阻塞,而不需要sleep一個固定的時間來等待.但是其缺點是無法指定固定的goroutine數目.可能通過使用channel解決此問題。