最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2018年07月24日 08:33:29   作者:domac的菜園子  
這篇文章主要給大家介紹了關(guān)于go語言同步教程之條件變量的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

Go的標(biāo)準(zhǔn)庫中有一個(gè)類型叫條件變量: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 函數(shù)可以返回 *sync.Cond 類型的結(jié)果, *sync.Cond 我們主要操作其三個(gè)方法,分別是:

wait():等待通知

Signal():?jiǎn)尾ネㄖ?/p>

Broadcast():廣播通知

具體的函數(shù)說明如下:

// 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本質(zhì)上是一些正在等待某個(gè)條件的線程的同步機(jī)制。

sync.Cond 主要實(shí)現(xiàn)一個(gè)條件變量,假如 goroutine A 執(zhí)行前需要等待另外的goroutine B 的通知,那邊處于等待的goroutine A 會(huì)保存在一個(gè)通知列表,也就是說需要某種變量狀態(tài)的goroutine A 將會(huì)等待/Wait在那里,當(dāng)某個(gè)時(shí)刻狀態(tài)改變時(shí)負(fù)責(zé)通知的goroutine B 通過對(duì)條件變量通知的方式(Broadcast,Signal)來通知處于等待條件變量的goroutine A, 這樣便可首先一種“消息通知”的同步機(jī)制。

以go的http處理為例,在Go的源碼中http模塊server部分源碼中所示,當(dāng)需要處理一個(gè)新的連接的時(shí)候,若連接conn是實(shí)現(xiàn)自*tls.Conn的情況下,會(huì)進(jìn)行相關(guān)的客戶端與服務(wù)端的“握手”處理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函數(shù)代碼通過使用條件變量的方式來處理新連接握手調(diào)用的同步問題:

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

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

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

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

package main

import (
 "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 {
  //讀取一個(gè)字節(jié)
  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()
 //寫入一個(gè)數(shù)據(jù)塊
 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)
 }
}

當(dāng)使用sync.Cond的時(shí)候有兩點(diǎn)移動(dòng)要注意的:

  • 一定要在調(diào)用cond.Wait方法前,鎖定與之關(guān)聯(lián)的讀寫鎖
  • 一定不要忘記在cond.Wait后,若數(shù)據(jù)已經(jīng)處理完畢,在返回前要對(duì)與之關(guān)聯(lián)的讀寫鎖進(jìn)行解鎖。

如下面 Wait() 的源碼所示,Cond.Wait會(huì)自動(dòng)釋放鎖等待信號(hào)的到來,當(dāng)信號(hào)到來后,第一個(gè)獲取到信號(hào)的Wait將繼續(xù)往下執(zhí)行并從新上鎖

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

如果不釋放鎖, 其它收到信號(hào)的gouroutine將阻塞無法繼續(xù)執(zhí)行。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Golang?依賴注入經(jīng)典解決方案uber/fx理論解析

    Golang?依賴注入經(jīng)典解決方案uber/fx理論解析

    這篇文章主要為大家介紹了Golang依賴注入經(jīng)典解決方案uber/fx理論解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • Go語言中結(jié)構(gòu)體方法副本傳參與指針傳參的區(qū)別介紹

    Go語言中結(jié)構(gòu)體方法副本傳參與指針傳參的區(qū)別介紹

    這篇文章主要給大家介紹了關(guān)于Go語言中結(jié)構(gòu)體方法副本傳參與指針傳參的區(qū)別的相關(guān)資料,文中先對(duì)GO語言結(jié)構(gòu)體方法跟結(jié)構(gòu)體指針方法的區(qū)別進(jìn)行了一些簡(jiǎn)單的介紹,來幫助大家理解學(xué)習(xí),需要的朋友可以參考下。
    2017-12-12
  • Go 1.22中的for循環(huán)新特性詳解

    Go 1.22中的for循環(huán)新特性詳解

    在 Go 語言中,for 循環(huán)是實(shí)現(xiàn)迭代的主要方式,Go 中的 for 循環(huán)非常靈活,有多種使用方式,本文將給大家詳細(xì)的介紹一下Go 1.22中的for循環(huán)新特性,感興趣的朋友可以參考下
    2024-02-02
  • GO利用channel協(xié)調(diào)協(xié)程的實(shí)現(xiàn)

    GO利用channel協(xié)調(diào)協(xié)程的實(shí)現(xiàn)

    本文主要介紹了GO利用channel協(xié)調(diào)協(xié)程的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 一文帶你深入理解Go語言中的sync.Cond

    一文帶你深入理解Go語言中的sync.Cond

    sync.Cond?表示的是條件變量,它是一種同步機(jī)制,用來協(xié)調(diào)多個(gè)?goroutine?之間的同步。本文將通過示例為大家介紹Go語言中sync.Cond的使用,需要的可以參考一下
    2023-01-01
  • GoLang 逃逸分析的機(jī)制詳解

    GoLang 逃逸分析的機(jī)制詳解

    這篇文章主要介紹了GoLang-逃逸分析的機(jī)制詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 淺談Go數(shù)組比切片好在哪

    淺談Go數(shù)組比切片好在哪

    Go1.17 會(huì)正式支持切片轉(zhuǎn)換到數(shù)據(jù),不再需要用以前那種騷辦法了,本文就談?wù)凣o數(shù)組比切片好在哪,感興趣的可以了解一下
    2021-09-09
  • Go語言基礎(chǔ)go build命令用法及示例詳解

    Go語言基礎(chǔ)go build命令用法及示例詳解

    這篇文章主要為大家介紹了Go語言基礎(chǔ)go build命令用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2021-11-11
  • Golang中的panic之避免和處理程序中的異常情況

    Golang中的panic之避免和處理程序中的異常情況

    Golang中的panic是一種異常處理機(jī)制,可以在程序出現(xiàn)異常情況時(shí)終止程序并打印錯(cuò)誤信息。為了避免panic對(duì)程序的影響,開發(fā)者可以采用一系列技巧,如defer+recover、編寫可重入的代碼、使用錯(cuò)誤返回值等。這些技巧可以幫助開發(fā)者優(yōu)雅地處理程序中的異常情況
    2023-04-04
  • go依賴注入庫samber/do使用示例講解

    go依賴注入庫samber/do使用示例講解

    這篇文章主要介紹了go依賴注入庫samber/do使用,在本文中,我們學(xué)習(xí)了如何使用samber/do在?Go?中提供依賴注入,需要的朋友可以參考下
    2024-02-02

最新評(píng)論

朝阳县| 柳州市| 靖江市| 松桃| 许昌县| 屏边| 洱源县| 石家庄市| 连平县| 竹溪县| 苏尼特左旗| 揭阳市| 固阳县| 莆田市| 繁峙县| 上思县| 堆龙德庆县| 长白| 韶山市| 麻阳| 霍林郭勒市| 景德镇市| 秭归县| 乌拉特中旗| 余江县| 沅陵县| 阳曲县| 灵山县| 铜山县| 东源县| 鄯善县| 勃利县| 青阳县| 东安县| 虞城县| 灵璧县| 深圳市| 海晏县| 吉安市| 宁陵县| 上思县|