select
-
select 的作用:
通過 select 可以監(jiān)聽 channel 上的數(shù)據(jù)流動辫愉。
-
select 的用法:
與 switch case 語法類似窍仰。但是 case 后面必須是 IO 操作,不可以人一些判斷表達(dá)式导匣。
select { case <-chan1: // 如果 chan1 成功讀到數(shù)據(jù),則進(jìn)行該 case 處理語句 case chan2 <- 1: //如果成功向 chan2 寫入數(shù)據(jù)茸时,則進(jìn)行該 case 處理語句 default: // 如果上面都沒有成功贡定,則進(jìn)入 default 處理流程 }
-
注意:
- 監(jiān)聽的 case 中,沒有滿足監(jiān)聽條件可都,阻塞缓待。
- 監(jiān)聽的 case 中,有多個滿足監(jiān)聽條件渠牲,任選一個執(zhí)行旋炒。
- 可以使用 default 來處理所有case 都不滿足監(jiān)聽條件的狀況。通常不用(會產(chǎn)生忙輪詢)签杈。
- select 自身不帶有循環(huán)機(jī)制瘫镇,需借助外層 for 來循環(huán)監(jiān)聽。
- break 跳出 select 中的 case 選項答姥。類似于 switch 中的用法铣除。
超時處理
有時候會出現(xiàn) goroutine 阻塞的情況,可以使用 select 來設(shè)置超時鹦付,通過如下的方式實現(xiàn):
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <-c:
fmt.Println(v)
case <time.After(5 * time.Second):
fmt.Println("timeout"):
o <- true
break
}
}
}()
// c <-66 // 注釋掉尚粘,引發(fā) timeout
<-o
}
鎖和條件變量
什么是鎖
就是某個協(xié)程(線程)在訪問某個資源時先鎖住,防止其他協(xié)程的訪問敲长,等訪問完畢解鎖后其他協(xié)程再來加鎖進(jìn)行訪問郎嫁。
死鎖
死鎖不是鎖的一種,是一種錯誤使用鎖導(dǎo)致的現(xiàn)象祈噪。
死鎖是指兩個或兩個以上的進(jìn)程在進(jìn)行過程中泽铛,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用钳降,他們都將無法推進(jìn)下去厚宰。此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖。
- 常見的場景:
- 單 go 程自己死鎖
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println("send")
go func() {
<- ch
fmt.Println("received")
}()
fmt.Println("over")
}
互斥鎖
每個資源都對應(yīng)一個可稱為 “互斥鎖” 的標(biāo)記,這個標(biāo)記用來保證在任意時刻铲觉,只能有一個協(xié)程(線程)訪問該資源澈蝙。其它的協(xié)程只能等待。
互斥鎖是傳統(tǒng)并發(fā)編程對共享資源進(jìn)行訪問控制的主要手段撵幽。它由標(biāo)準(zhǔn)庫 sync
中的 Mutex
結(jié)構(gòu)體類型表示灯荧。 sync.Mutex
類型只有兩個公開的指針方法,Lock 和 Unlock盐杂。Lock 鎖定當(dāng)前的共享資源逗载,Unlock 進(jìn)行解鎖。
在使用互斥鎖時链烈,一定要注意:對資源操作完成后厉斟,一定要解鎖,否則會出現(xiàn)流程執(zhí)行異常强衡、死鎖等問題擦秽。通常借助 defer
。鎖定后漩勤,立即使用 defer
語句保證互斥鎖及時解鎖感挥。如下所示:
var mutex sync.Mutex
func write() {
mutex.Lock()
defer mutex.Unlock()
}
讀寫鎖
互斥鎖的本質(zhì)是當(dāng)一個 goroutine 訪問的時候,其他 goroutine 都不能訪問越败。這樣在資源同步触幼,避免競爭的同時也降低了程序的并發(fā)性能。程序由原來的并行執(zhí)行變成了串行執(zhí)行究飞。
讀寫鎖可以讓多個讀操作并發(fā)置谦,同時讀取,但是對于寫操作是完全互斥的噪猾。也就是說霉祸,當(dāng)一個 goroutine 進(jìn)行寫操作的時候,其他 goroutine 既不能進(jìn)行讀操作袱蜡,也不能進(jìn)行寫操作丝蹭。
讀時共享,寫時獨(dú)占坪蚁。寫鎖優(yōu)先級比讀鎖高奔穿。
-
“寫鎖定”和“讀鎖定”
func (*RWMutex) Lock() func (*RWMutex) Unlock()
-
“讀鎖定”和“寫鎖定”
func (*RWMutex) RLock() func (*RWMutex) RUnlock()
條件變量
條件變量的作用并不能保證在同一時刻僅有一個協(xié)程(線程)訪問某個共享的數(shù)據(jù)資源,而是在對應(yīng)的共享數(shù)據(jù)的狀態(tài)發(fā)生變化時敏晤,通知阻塞在某個條件上的協(xié)程(線程)贱田。條件變量不是鎖,在并發(fā)中不能達(dá)到同步的目的嘴脾,因此條件變量總是與鎖一塊使用男摧。
Go 標(biāo)準(zhǔn)庫中的 sys.Cond
類型代表了條件變量蔬墩。條件變量要與鎖(互斥鎖或者讀寫鎖)一起使用。成員變量 L
代表與條件變量搭配使用的鎖耗拓。
type Cond struct {
noCopy noCopy
L Locker
notify notifyList
checker copyChecker
}
- 對應(yīng)的 3 個常用方法拇颅,Wait、Signal乔询、Broadcast:
-
func (c *Cond) Wait()
該函數(shù)的作用:- 阻塞等待條件變量滿足
- 釋放已掌握的互斥鎖樟插,相當(dāng)于
cond.Unlock()
。注意:兩步為原子操作(不會被線程調(diào)度機(jī)制打斷的操作竿刁;這種操作一旦開始黄锤,就一直運(yùn)行到結(jié)束)。 - 當(dāng)被喚醒食拜,
Wait()
函數(shù)返回時鸵熟,解除阻塞并重新獲取互斥鎖。相當(dāng)于cond.Lock()
-
func (c *Cond) Signal()
單發(fā)通知负甸,給一個正等待(阻塞)在該條件變量上的 goroutine(線程)發(fā)送通知旅赢。 -
func (c *Cond) Broadcast()
廣播通知,給正在等待(阻塞)在該條件變量上的所有 goroutine(線程)發(fā)送通知惑惶。
-
- 使用流程:
- 創(chuàng)建條件變量:
var cond sync.Cond
- 指定條件變量用的鎖:
cond.L = new(sync.Mutex)
-
cond.Lock()
給公共區(qū)加鎖(互斥量) - 判斷是否達(dá)到阻塞條件(緩沖區(qū)滿/空) --- for 循環(huán)判斷
- 訪問公共區(qū) ---讀、寫數(shù)據(jù)短纵、打印
- 解鎖條件變量用的鎖
cond.L.Unlock()
- 喚醒阻塞在條件變量上的對端带污。
signal() Broadcast()
- 創(chuàng)建條件變量: