Go 語言學(xué)習(xí)筆記-select、鎖和條件變量

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ā)送通知惑惶。
  • 使用流程:
    1. 創(chuàng)建條件變量:var cond sync.Cond
    2. 指定條件變量用的鎖:cond.L = new(sync.Mutex)
    3. cond.Lock() 給公共區(qū)加鎖(互斥量)
    4. 判斷是否達(dá)到阻塞條件(緩沖區(qū)滿/空) --- for 循環(huán)判斷
    5. 訪問公共區(qū) ---讀、寫數(shù)據(jù)短纵、打印
    6. 解鎖條件變量用的鎖 cond.L.Unlock()
    7. 喚醒阻塞在條件變量上的對端带污。signal() Broadcast()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市香到,隨后出現(xiàn)的幾起案子鱼冀,更是在濱河造成了極大的恐慌,老刑警劉巖悠就,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件千绪,死亡現(xiàn)場離奇詭異,居然都是意外死亡梗脾,警方通過查閱死者的電腦和手機(jī)荸型,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來炸茧,“玉大人瑞妇,你說我怎么就攤上這事∷蠊冢” “怎么了辕狰?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長控漠。 經(jīng)常有香客問我蔓倍,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任偶翅,我火速辦了婚禮默勾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘倒堕。我一直安慰自己灾测,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布垦巴。 她就那樣靜靜地躺著媳搪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骤宣。 梳的紋絲不亂的頭發(fā)上秦爆,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音憔披,去河邊找鬼等限。 笑死,一個胖子當(dāng)著我的面吹牛芬膝,可吹牛的內(nèi)容都是我干的望门。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锰霜,長吁一口氣:“原來是場噩夢啊……” “哼筹误!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起癣缅,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤厨剪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后友存,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祷膳,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年屡立,在試婚紗的時候發(fā)現(xiàn)自己被綠了直晨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡侠驯,死狀恐怖抡秆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吟策,我是刑警寧澤儒士,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站檩坚,受9級特大地震影響着撩,放射性物質(zhì)發(fā)生泄漏诅福。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一拖叙、第九天 我趴在偏房一處隱蔽的房頂上張望氓润。 院中可真熱鬧,春花似錦薯鳍、人聲如沸咖气。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽崩溪。三九已至,卻和暖如春斩松,著一層夾襖步出監(jiān)牢的瞬間伶唯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工惧盹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乳幸,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓钧椰,卻偏偏與公主長得像粹断,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嫡霞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容