自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go中多協(xié)程協(xié)作之Sync.Cond

開(kāi)發(fā) 后端
在Go中協(xié)程間通信的方式有多種,最常用的是channel。如果牽扯多個(gè)協(xié)程的通知,可以使用sync.Cond。

[[400826]]

1. 程序中的通信方式

GO語(yǔ)言中有句名言:“不要用共享內(nèi)存來(lái)通信,而是使用通信來(lái)共享內(nèi)存”。

編程語(yǔ)言中,通信方式分為進(jìn)程間通信、線程間通信。

1.進(jìn)程間通信,常用方式:

  • 有名管道
  • 無(wú)名管道
  • 信號(hào)
  • 共享內(nèi)存
  • 消息隊(duì)列
  • 信號(hào)燈集
  • socket

2.線程間通信,常用方式:

  • 信號(hào)量
  • 互斥鎖
  • 條件變量

對(duì)于Go語(yǔ)言來(lái)說(shuō),Go程序啟動(dòng)之后對(duì)外是一個(gè)進(jìn)程,內(nèi)部包含若干協(xié)程,協(xié)程相當(dāng)于用戶態(tài)輕量級(jí)線程,所以協(xié)程的通信方式大多可以使用線程間通信方式來(lái)完成。

協(xié)程間通信方式,官方推薦使用channel,channel在一對(duì)一的協(xié)程之間進(jìn)行數(shù)據(jù)交換與通信十分便捷。但是,一對(duì)多的廣播場(chǎng)景中,則顯得有點(diǎn)無(wú)力,此時(shí)就需要sync.Cond來(lái)輔助。

2. 什么是廣播?

舉個(gè)例子,上高中時(shí),宿管老師每天早晨需要叫醒學(xué)生們?nèi)ド险n。這個(gè)時(shí)候,有兩種解決方法:①一個(gè)寢室一個(gè)寢室的把學(xué)生叫醒。②在宿舍樓安裝個(gè)廣播,到起床時(shí)間時(shí),在廣播上叫醒學(xué)生。顯然,使用廣播的方式效率更高。

編程中的廣播可以理解為:多個(gè)操作流程依賴于一個(gè)操作流程完成后才能進(jìn)行某種動(dòng)作,這個(gè)被依賴的操作流程在喚醒所有依賴者時(shí)使用的一種通知方式。

在Go語(yǔ)言中,則可以使用sync.Cond來(lái)實(shí)現(xiàn)多個(gè)協(xié)程之間的廣播通知功能。

3. sync.Cond

cond是sync包下面的一種數(shù)據(jù)類(lèi)型,相當(dāng)于線程間通信的條件變量方式。

  1. // Cond implements a condition variable, a rendezvous point 
  2. // for goroutines waiting for or announcing the occurrence 
  3. // of an event. 
  4. // 
  5. // Each Cond has an associated Locker L (often a *Mutex or *RWMutex), 
  6. // which must be held when changing the condition and 
  7. // when calling the Wait method. 
  8. // 
  9. // A Cond must not be copied after first use. 
  10. type Cond struct { 
  11.     noCopy noCopy  // 在第一次使用后不可復(fù)制,使用go vet作為檢測(cè)使用 
  12.  
  13.     // L is held while observing or changing the condition 
  14.   // 根據(jù)需求初始化不同的鎖,如*Mutex 和 *RWMutex。注意是 指針類(lèi)型 
  15.     L Locker 
  16.  
  17.   // 具有頭尾指針的鏈表。存儲(chǔ)被阻塞的協(xié)程,通知時(shí)操作該鏈表中的協(xié)程 
  18.     notify  notifyList 
  19.     checker copyChecker  // 復(fù)制檢查,檢查cond實(shí)例是否被復(fù)制 

 該數(shù)據(jù)類(lèi)型提供的方法有:

  1. type Cond 
  2.  
  3. func NewCond(l Locker) *Cond 
  4. func (c *Cond) Broadcast() // 通知所有協(xié)程,廣播 
  5. func (c *Cond) Signal()  // 通知一個(gè)協(xié)程 
  6. func (c *Cond) Wait()  // 阻塞等待,直到被喚醒 

 對(duì)應(yīng)源碼追溯

  1. // Wait atomically unlocks c.L and suspends execution 
  2. // of the calling goroutine. After later resuming execution, 
  3. // Wait locks c.L before returning. Unlike in other systems, 
  4. // Wait cannot return unless awoken by Broadcast or Signal. 
  5. // 
  6. // Because c.L is not locked when Wait first resumes, the caller 
  7. // typically cannot assume that the condition is true when 
  8. // Wait returnsInstead, the caller should Wait in a loop: 
  9. //       
  10. //      注意下面的寫(xiě)法是官方推薦的 
  11. //    c.L.Lock() 
  12. //    for !condition() { 
  13. //        c.Wait() 
  14. //    } 
  15. //    ... make use of condition ... 
  16. //    c.L.Unlock() 
  17. // 
  18. func (c *Cond) Wait() { 
  19.     // 檢查c是否是被復(fù)制的,如果是就panic 
  20.     c.checker.check() 
  21.     // 獲取等待隊(duì)列的一個(gè)ticket數(shù)值,作為喚醒時(shí)的一個(gè)令牌憑證 
  22.     t := runtime_notifyListAdd(&c.notify) 
  23.     // 解鎖 
  24.     c.L.Unlock() 
  25.    
  26.     // 注意,上面的ticket數(shù)值會(huì)作為阻塞攜程的一個(gè)標(biāo)識(shí) 
  27.     // 加入通知隊(duì)列里面 
  28.     // 到這里執(zhí)行g(shù)opark(),當(dāng)前協(xié)程掛起,直到signal或broadcast發(fā)起通知 
  29.     runtime_notifyListWait(&c.notify, t) 
  30.    
  31.     // 被喚醒之后,先獲取鎖 
  32.     c.L.Lock() 
  33.  
  34. // Signal wakes one goroutine waiting on c, if there is any
  35. // 
  36. // It is allowed but not required for the caller to hold c.L 
  37. // during the call. 
  38. func (c *Cond) Signal() { 
  39.     c.checker.check() 
  40.     runtime_notifyListNotifyOne(&c.notify)  // 隨機(jī)挑選一個(gè)進(jìn)行通知,wait阻塞解除 
  41.  
  42. // Broadcast wakes all goroutines waiting on c. 
  43. // 
  44. // It is allowed but not required for the caller to hold c.L 
  45. // during the call. 
  46. func (c *Cond) Broadcast() { 
  47.     c.checker.check() 
  48.     // 通知所有阻塞等待的協(xié)程 
  49.     // 主要是喚醒 cond.notify 鏈表上的各個(gè)協(xié)程 
  50.     runtime_notifyListNotifyAll(&c.notify) 

 使用方法,代碼示例:

  1. var locker sync.Mutex 
  2. var cond = sync.NewCond(&locker) 
  3.  
  4. // NewCond(l Locker)里面定義的是一個(gè)接口,擁有l(wèi)ock和unlock方法。 
  5. // 看到sync.Mutex的方法,func (m *Mutex) Lock(),可以看到是指針有這兩個(gè)方法,所以應(yīng)該傳遞的是指針 
  6. func main() { 
  7.     // 啟動(dòng)多個(gè)協(xié)程 
  8.     for i := 0; i < 10; i++ { 
  9.         gofunc(x int) { 
  10.             cond.L.Lock()          // 獲取鎖 
  11.             defer cond.L.Unlock()  // 釋放鎖 
  12.            
  13.             cond.Wait()   // 等待通知,阻塞當(dāng)前 goroutine 
  14.            
  15.             // 通知到來(lái)的時(shí)候, cond.Wait()就會(huì)結(jié)束阻塞, do something. 這里僅打印 
  16.             fmt.Println(x) 
  17.         }(i) 
  18.     } 
  19.    
  20.     time.Sleep(time.Second * 1) // 睡眠 1 秒,等待所有 goroutine 進(jìn)入 Wait 阻塞狀態(tài) 
  21.     fmt.Println("Signal..."
  22.     cond.Signal()               // 1 秒后下發(fā)一個(gè)通知給已經(jīng)獲取鎖的 goroutine 
  23.    
  24.     time.Sleep(time.Second * 1) 
  25.     fmt.Println("Signal..."
  26.     cond.Signal()               // 1 秒后下發(fā)下一個(gè)通知給已經(jīng)獲取鎖的 goroutine 
  27.    
  28.     time.Sleep(time.Second * 1) 
  29.     cond.Broadcast()            // 1 秒后下發(fā)廣播給所有等待的goroutine 
  30.     fmt.Println("Broadcast..."
  31.     time.Sleep(time.Second * 1) // 等待所有 goroutine 執(zhí)行完畢 

 總結(jié)

在Go中協(xié)程間通信的方式有多種,最常用的是channel。如果牽扯多個(gè)協(xié)程的通知,可以使用sync.Cond。

查看channel、sync.Cond源碼之后會(huì)發(fā)現(xiàn),它們有相似之處:

  1. 阻塞協(xié)程統(tǒng)一被封裝在 sudog 結(jié)構(gòu)里面
  2. channel阻塞讀/寫(xiě)時(shí),用雙向鏈表存儲(chǔ)被阻塞導(dǎo)致等待喚醒的協(xié)程
  3. sync.Cond用帶有頭尾指針的單向鏈表存儲(chǔ)被阻塞導(dǎo)致等待喚醒的協(xié)程
  4. 阻塞時(shí)都是使用gopark()進(jìn)行協(xié)程的掛起操作

雖說(shuō)有相似之處,但是卻有本質(zhì)區(qū)別:

  1. channel 可以用來(lái)在協(xié)程間傳遞數(shù)據(jù)
  2. sync.Cond 不可以在協(xié)程間傳遞數(shù)據(jù),主要用來(lái)進(jìn)行協(xié)程的阻塞喚醒操作。如果需要傳遞數(shù)據(jù),則需要全局變量進(jìn)行傳遞

 

責(zé)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2023-06-26 08:28:35

Sync.CondGolang

2021-07-06 07:46:07

Go語(yǔ)言編程

2023-11-28 08:01:48

互斥鎖共享資源

2023-07-13 08:06:05

應(yīng)用協(xié)程阻塞

2016-10-28 17:39:47

phpgolangcoroutine

2021-09-27 23:28:29

Go多協(xié)程并發(fā)

2018-12-04 14:00:41

協(xié)程編程模式PHP

2024-12-03 15:15:22

2023-07-27 13:46:10

go開(kāi)源項(xiàng)目

2021-04-25 09:36:20

Go協(xié)程線程

2024-06-27 07:56:49

2024-05-29 08:05:15

Go協(xié)程通信

2025-02-28 09:04:08

2021-05-20 09:14:09

Kotlin協(xié)程掛起和恢復(fù)

2021-09-16 09:59:13

PythonJavaScript代碼

2022-10-28 10:45:22

Go協(xié)程GoFrame

2021-08-04 16:19:55

AndroidKotin協(xié)程Coroutines

2024-08-27 09:46:39

Go協(xié)程效率

2022-09-12 06:35:00

C++協(xié)程協(xié)程狀態(tài)

2021-02-19 06:56:33

架構(gòu)協(xié)程應(yīng)用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)