国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Golang > 正文

go語言同步教程之條件變量

2020-04-01 18:55:33
字體:
來源:轉載
供稿:網友

Go的標準庫中有一個類型叫條件變量:sync.Cond。這種類型與互斥鎖和讀寫鎖不同,它不是開箱即用的,它需要與互斥鎖組合使用:

// NewCond returns a new Cond with Locker l.func NewCond(l Locker) *Cond { return &Cond{L: l}}// A Locker represents an object that can be locked and unlocked.type Locker interface { Lock() Unlock()}

通過使用 NewCond 函數可以返回 *sync.Cond 類型的結果, *sync.Cond 我們主要操作其三個方法,分別是:

wait():等待通知

Signal():單播通知

Broadcast():廣播通知

具體的函數說明如下:

// Wait atomically unlocks c.L and suspends execution// of the calling goroutine. After later resuming execution,// Wait locks c.L before returning. Unlike in other systems,// Wait cannot return unless awoken by Broadcast or Signal.//// Because c.L is not locked when Wait first resumes, the caller// typically cannot assume that the condition is true when// Wait returns. Instead, the caller should Wait in a loop://// c.L.Lock()// for !condition() {//  c.Wait()// }// ... make use of condition ...// c.L.Unlock()//func (c *Cond) Wait() { c.checker.check() t := runtime_notifyListAdd(&c.notify) c.L.Unlock() runtime_notifyListWait(&c.notify, t) c.L.Lock()}// Signal wakes one goroutine waiting on c, if there is any.//// It is allowed but not required for the caller to hold c.L// during the call.func (c *Cond) Signal() { c.checker.check() runtime_notifyListNotifyOne(&c.notify)}// Broadcast wakes all goroutines waiting on c.//// It is allowed but not required for the caller to hold c.L// during the call.func (c *Cond) Broadcast() { c.checker.check() runtime_notifyListNotifyAll(&c.notify)}

條件變量sync.Cond本質上是一些正在等待某個條件的線程的同步機制。

sync.Cond 主要實現一個條件變量,假如 goroutine A 執行前需要等待另外的goroutine B 的通知,那邊處于等待的goroutine A 會保存在一個通知列表,也就是說需要某種變量狀態的goroutine A 將會等待/Wait在那里,當某個時刻狀態改變時負責通知的goroutine B 通過對條件變量通知的方式(Broadcast,Signal)來通知處于等待條件變量的goroutine A, 這樣便可首先一種“消息通知”的同步機制。

以go的http處理為例,在Go的源碼中http模塊server部分源碼中所示,當需要處理一個新的連接的時候,若連接conn是實現自*tls.Conn的情況下,會進行相關的客戶端與服務端的“握手”處理Handshake(), 入口代碼如下:

if tlsConn, ok := c.rwc.(*tls.Conn); ok {  if d := c.server.ReadTimeout; d != 0 {   c.rwc.SetReadDeadline(time.Now().Add(d))  }  if d := c.server.WriteTimeout; d != 0 {   c.rwc.SetWriteDeadline(time.Now().Add(d))  }  if err := tlsConn.Handshake(); err != nil {   c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)   return  }  c.tlsState = new(tls.ConnectionState)  *c.tlsState = tlsConn.ConnectionState()  if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {   if fn := c.server.TLSNextProto[proto]; fn != nil {    h := initNPNRequest{tlsConn, serverHandler{c.server}}    fn(c.server, tlsConn, h)   }   return  } }

其中的Handshake函數代碼通過使用條件變量的方式來處理新連接握手調用的同步問題:

func (c *Conn) Handshake() error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() for {  if err := c.handshakeErr; err != nil {   return err  }  if c.handshakeComplete {   return nil  }  if c.handshakeCond == nil {   break  }  c.handshakeCond.Wait() } c.handshakeCond = sync.NewCond(&c.handshakeMutex) c.handshakeMutex.Unlock() c.in.Lock() defer c.in.Unlock() c.handshakeMutex.Lock() if c.handshakeErr != nil || c.handshakeComplete {  panic("handshake should not have been able to complete after handshakeCond was set") } if c.isClient {  c.handshakeErr = c.clientHandshake() } else {  c.handshakeErr = c.serverHandshake() } if c.handshakeErr == nil {  c.handshakes++ } else {  c.flush() } if c.handshakeErr == nil && !c.handshakeComplete {  panic("handshake should have had a result.") } c.handshakeCond.Broadcast() c.handshakeCond = nil return c.hand

我們也可以再通過一個例子熟悉sync.Cond的使用:

我們嘗試實現一個讀寫同步的例子,需求是:我們有數個讀取器和數個寫入器,讀取器必須依賴寫入器對緩存區進行數據寫入后,才可從緩存區中對數據進行讀出。我們思考下,要實現類似的功能,除了使用channel,還能如何做?

寫入器每次完成寫入數據后,它都需要某種通知機制廣播給處于阻塞狀態的讀取器,告訴它們可以對數據進行訪問,這其實跟sync.Cond 的 廣播機制是不是很像? 有了這個廣播機制,我們可以通過sync.Cond來實現這個例子了:

package mainimport ( "bytes" "fmt" "io" "sync" "time")type MyDataBucket struct { br  *bytes.Buffer gmutex *sync.RWMutex rcond *sync.Cond //讀操作需要用到的條件變量}func NewDataBucket() *MyDataBucket { buf := make([]byte, 0) db := &MyDataBucket{  br:  bytes.NewBuffer(buf),  gmutex: new(sync.RWMutex), } db.rcond = sync.NewCond(db.gmutex.RLocker()) return db}func (db *MyDataBucket) Read(i int) { db.gmutex.RLock() defer db.gmutex.RUnlock() var data []byte var d byte var err error for {  //讀取一個字節  if d, err = db.br.ReadByte(); err != nil {   if err == io.EOF {    if string(data) != "" {     fmt.Printf("reader-%d: %s/n", i, data)    }    db.rcond.Wait()    data = data[:0]    continue   }  }  data = append(data, d) }}func (db *MyDataBucket) Put(d []byte) (int, error) { db.gmutex.Lock() defer db.gmutex.Unlock() //寫入一個數據塊 n, err := db.br.Write(d) db.rcond.Broadcast() return n, err}func main() { db := NewDataBucket() go db.Read(1) go db.Read(2) for i := 0; i < 10; i++ {  go func(i int) {   d := fmt.Sprintf("data-%d", i)   db.Put([]byte(d))  }(i)  time.Sleep(100 * time.Millisecond) }}

當使用sync.Cond的時候有兩點移動要注意的:

  • 一定要在調用cond.Wait方法前,鎖定與之關聯的讀寫鎖
  • 一定不要忘記在cond.Wait后,若數據已經處理完畢,在返回前要對與之關聯的讀寫鎖進行解鎖。

如下面 Wait() 的源碼所示,Cond.Wait會自動釋放鎖等待信號的到來,當信號到來后,第一個獲取到信號的Wait將繼續往下執行并從新上鎖

func (c *Cond) Wait() { c.checker.check() t := runtime_notifyListAdd(&c.notify) c.L.Unlock() runtime_notifyListWait(&c.notify, t) c.L.Lock()}

如果不釋放鎖, 其它收到信號的gouroutine將阻塞無法繼續執行。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 小金县| 永城市| 福贡县| 新源县| 绥棱县| 库尔勒市| 土默特右旗| 拉孜县| 方城县| 德江县| 探索| 澳门| 北川| 克拉玛依市| 淄博市| 黄冈市| 达孜县| 霍州市| 页游| 都匀市| 胶州市| 阿尔山市| 措美县| 兴城市| 德庆县| 台北县| 武安市| 济南市| 碌曲县| 南和县| 鄂托克前旗| 红河县| 衡山县| 海伦市| 鸡东县| 渭南市| 吐鲁番市| 阿城市| 叙永县| 东乌| 天镇县|