欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言并發(fā)編程之互斥鎖Mutex和讀寫鎖RWMutex

 更新時(shí)間:2021年10月08日 15:21:34   作者:Valine  
Go 語言中提供了很多同步工具,本文將介紹互斥鎖Mutex和讀寫鎖RWMutex的使用方法,想要具體了解的小伙伴,請(qǐng)參考下面文章詳細(xì)內(nèi)容,希望對(duì)你有所幫助

在并發(fā)編程中,多個(gè)Goroutine訪問同一塊內(nèi)存資源時(shí)可能會(huì)出現(xiàn)競(jìng)態(tài)條件,我們需要在臨界區(qū)中使用適當(dāng)?shù)耐讲僮鱽硪员苊飧?jìng)態(tài)條件。Go 語言中提供了很多同步工具,本文將介紹互斥鎖Mutex和讀寫鎖RWMutex的使用方法。

一、互斥鎖Mutex

1、Mutex介紹

Go 語言的同步工具主要由 sync 包提供,互斥鎖 (Mutex) 與讀寫鎖 (RWMutex) 就是sync 包中的方法。

互斥鎖可以用來保護(hù)一個(gè)臨界區(qū),保證同一時(shí)刻只有一個(gè) goroutine 處于該臨界區(qū)內(nèi)。主要包括鎖定(Lock方法)和解鎖(Unlock方法)兩個(gè)操作,首先對(duì)進(jìn)入臨界區(qū)的goroutine進(jìn)行鎖定,離開時(shí)進(jìn)行解鎖。

使用互斥鎖 (Mutex)時(shí)要注意以下幾點(diǎn):

  • 不要重復(fù)鎖定互斥鎖,否則會(huì)阻塞,也可能會(huì)導(dǎo)致死鎖(deadlock);
  • 要對(duì)互斥鎖進(jìn)行解鎖,這也是為了避免重復(fù)鎖定;
  • 不要對(duì)未鎖定或者已解鎖的互斥鎖解鎖;
  • 不要在多個(gè)函數(shù)之間直接傳遞互斥鎖,sync.Mutex類型屬于值類型,將它傳給一個(gè)函數(shù)時(shí),會(huì)產(chǎn)生一個(gè)副本,在函數(shù)中對(duì)鎖的操作不會(huì)影響原鎖

總之,一個(gè)互斥鎖只用來保護(hù)一個(gè)臨界區(qū),加鎖后記得解鎖,對(duì)于每一個(gè)鎖定操作,都要有且只有一個(gè)對(duì)應(yīng)的解鎖操作,也就是加鎖和解鎖要成對(duì)出現(xiàn),最保險(xiǎn)的做法時(shí)使用 defer語句 解鎖。

2、Mutex使用實(shí)例

下面的代碼模擬取錢和存錢操作:

package main

import (
 "flag"
 "fmt"
 "sync"
)

var (
    mutex   sync.Mutex
    balance int
    protecting uint  // 是否加鎖
    sign = make(chan struct{}, 10) //通道,用于等待所有g(shù)oroutine
)

// 存錢
func deposit(value int) {
    defer func() {
        sign <- struct{}{}
    }()

    if protecting == 1 {
        mutex.Lock()
        defer mutex.Unlock()
    }

    fmt.Printf("余額: %d\n", balance)
    balance += value
    fmt.Printf("存 %d 后的余額: %d\n", value, balance)
    fmt.Println()

}

// 取錢
func withdraw(value int) {
    defer func() {
        sign <- struct{}{}
    }()
    
    if protecting == 1 {
        mutex.Lock()
        defer mutex.Unlock()
    }

    fmt.Printf("余額: %d\n", balance)
    balance -= value
    fmt.Printf("取 %d 后的余額: %d\n", value, balance)
    fmt.Println()

}

func main() {
    
    for i:=0; i < 5; i++ {
        go withdraw(500) // 取500
        go deposit(500)  // 存500
    }

    for i := 0; i < 10; i++ {
  <-sign
 }
    fmt.Printf("當(dāng)前余額: %d\n", balance)
}

func init() {
    balance = 1000 // 初始賬戶余額為1000
    flag.UintVar(&protecting, "protecting", 0, "是否加鎖,0表示不加鎖,1表示加鎖")
}

上面的代碼中,使用了通道來讓主 goroutine 等待其他 goroutine 運(yùn)行結(jié)束,每個(gè)子goroutine在運(yùn)行結(jié)束之前向通道發(fā)送一個(gè)元素,主 goroutine 在最后從這個(gè)通道接收元素,接收次數(shù)與子goroutine個(gè)數(shù)相同。接收完后就會(huì)退出主goroutine

代碼使用協(xié)程實(shí)現(xiàn)多次(5次)對(duì)一個(gè)賬戶進(jìn)行存錢和取錢的操作,先來看不加鎖的情況:

余額: 1000
存 500 后的余額: 1500

余額: 1000
取 500 后的余額: 1000

余額: 1000
存 500 后的余額: 1500

余額: 1000
取 500 后的余額: 1000

余額: 1000
存 500 后的余額: 1500

余額: 1000
取 500 后的余額: 1000

余額: 1000
取 500 后的余額: 500

余額: 1000
存 500 后的余額: 1000

余額: 1000
取 500 后的余額: 500

余額: 1000
存 500 后的余額: 1000

當(dāng)前余額: 1000

可以看到出現(xiàn)了混亂,比如第二次1000的余額取500后還是1000,這種對(duì)同一資源的競(jìng)爭出現(xiàn)了競(jìng)態(tài)條件(Race Condition)。

下面來看加鎖的執(zhí)行結(jié)果:

余額: 1000
取 500 后的余額: 500

余額: 500
存 500 后的余額: 1000

余額: 1000
取 500 后的余額: 500

余額: 500
存 500 后的余額: 1000

余額: 1000
取 500 后的余額: 500

余額: 500
存 500 后的余額: 1000

余額: 1000
存 500 后的余額: 1500

余額: 1500
取 500 后的余額: 1000

余額: 1000
取 500 后的余額: 500

余額: 500
存 500 后的余額: 1000

當(dāng)前余額: 1000

加鎖后就正常了。

下面介紹更細(xì)化的互斥鎖:讀/寫互斥鎖RWMutex。

二、讀寫鎖RWMutex

1、RWMutex介紹

讀/寫互斥鎖RWMutex包含了讀鎖和寫鎖,分別對(duì)共享資源的“讀操作”和“寫操作”進(jìn)行保護(hù)。sync.RWMutex類型中的Lock方法和Unlock方法分別用于對(duì)寫鎖進(jìn)行鎖定和解鎖,而它的RLock方法和RUnlock方法則分別用于對(duì)讀鎖進(jìn)行鎖定和解鎖。

有了互斥鎖Mutex,為什么還需要讀寫鎖呢?因?yàn)樵诤芏嗖l(fā)操作中,并發(fā)讀取占比很大,寫操作相對(duì)較少,讀寫鎖可以并發(fā)讀取,這樣可以提供服務(wù)性能。讀寫鎖具有以下特征:

讀寫鎖 讀鎖 寫鎖
讀鎖 Yes No
寫鎖 No No

也就是說,

  • 如果某個(gè)共享資源受到讀鎖和寫鎖保護(hù)時(shí),其它goroutine不能進(jìn)行寫操作。換句話說就是讀寫操作和寫寫操作不能并行執(zhí)行,也就是讀寫互斥;
  • 受讀鎖保護(hù)時(shí),可以同時(shí)進(jìn)行多個(gè)讀操作。

在使用讀寫鎖時(shí),還需要注意:

  • 不要對(duì)未鎖定的讀寫鎖解鎖;
  • 對(duì)讀鎖不能使用寫鎖解鎖
  • 對(duì)寫鎖不能使用讀鎖解鎖

2、RWMutex使用實(shí)例

改寫前面的取錢和存錢操作,添加查詢余額的方法:

package main

import (
 "fmt"
 "sync"
)

// account 代表計(jì)數(shù)器。
type account struct {
 num uint         // 操作次數(shù)
 balance int   // 余額
 rwMu  *sync.RWMutex // 讀寫鎖
}

var sign = make(chan struct{}, 15) //通道,用于等待所有g(shù)oroutine

// 查看余額:使用讀鎖
func (c *account) check() {
 defer func() {
        sign <- struct{}{}
    }()
 c.rwMu.RLock()
 defer c.rwMu.RUnlock()
 fmt.Printf("%d 次操作后的余額: %d\n", c.num, c.balance)
}

// 存錢:寫鎖
func (c *account) deposit(value int) {
 defer func() {
        sign <- struct{}{}
    }()
    c.rwMu.Lock()
 defer c.rwMu.Unlock() 

 fmt.Printf("余額: %d\n", c.balance)   
 c.num += 1
    c.balance += value
    fmt.Printf("存 %d 后的余額: %d\n", value, c.balance)
    fmt.Println() 
}

// 取錢:寫鎖
func (c *account) withdraw(value int) {
    defer func() {
        sign <- struct{}{}
    }()
 c.rwMu.Lock()
 defer c.rwMu.Unlock()   
 fmt.Printf("余額: %d\n", c.balance)     
 c.num += 1
    c.balance -= value
 fmt.Printf("取 %d 后的余額: %d\n", value, c.balance)
    fmt.Println()  
}


func main() {
 c := account{0, 1000, new(sync.RWMutex)}

 for i:=0; i < 5; i++ {
        go c.withdraw(500) // 取500
        go c.deposit(500)  // 存500
  go c.check()
    }

    for i := 0; i < 15; i++ {
  <-sign
 }
 fmt.Printf("%d 次操作后的余額: %d\n", c.num, c.balance)

}

執(zhí)行結(jié)果:

余額: 1000
取 500 后的余額: 500

1 次操作后的余額: 500
1 次操作后的余額: 500
1 次操作后的余額: 500
1 次操作后的余額: 500
1 次操作后的余額: 500
余額: 500
存 500 后的余額: 1000

余額: 1000
取 500 后的余額: 500

余額: 500
存 500 后的余額: 1000

余額: 1000
存 500 后的余額: 1500

余額: 1500
取 500 后的余額: 1000

余額: 1000
取 500 后的余額: 500

余額: 500
存 500 后的余額: 1000

余額: 1000
取 500 后的余額: 500

余額: 500
存 500 后的余額: 1000

10 次操作后的余額: 1000

讀寫鎖和互斥鎖的不同之處在于讀寫鎖把對(duì)共享資源的讀操作和寫操作分開了,可以實(shí)現(xiàn)更復(fù)雜的訪問控制。

總結(jié):

讀寫鎖也是一種互斥鎖,它是互斥鎖的擴(kuò)展。在使用時(shí)需要注意:

  • 加鎖后一定要解鎖
  • 不要重復(fù)加鎖或者解鎖
  • 不解鎖未鎖定的鎖
  • 不要傳遞互斥鎖

到此這篇關(guān)于Go語言并發(fā)編程之互斥鎖Mutex和讀寫鎖RWMutex的文章就介紹到這了,更多相關(guān)Go語言 Mutex RWMutex內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言kafka生產(chǎn)消費(fèi)消息實(shí)例搬磚

    Go語言kafka生產(chǎn)消費(fèi)消息實(shí)例搬磚

    這篇文章主要為大家介紹了Go語言kafka生產(chǎn)消費(fèi)消息的實(shí)例搬磚,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • RoaringBitmap原理及在Go中的使用詳解

    RoaringBitmap原理及在Go中的使用詳解

    這篇文章主要為大家介紹了RoaringBitmap原理及在Go中的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • golang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關(guān)于golang的httpserver優(yōu)雅重啟的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • Golang Mutex實(shí)現(xiàn)互斥的具體方法

    Golang Mutex實(shí)現(xiàn)互斥的具體方法

    Mutex是Golang常見的并發(fā)原語,在開發(fā)過程中經(jīng)常使用到,本文主要介紹了Golang Mutex實(shí)現(xiàn)互斥的具體方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-04-04
  • GO語言中通道和sync包的使用教程分享

    GO語言中通道和sync包的使用教程分享

    這篇文章主要為大家詳細(xì)介紹了Go語言中通道和sync包的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語言有一定的幫助,需要的可以參考一下
    2023-02-02
  • Golang錯(cuò)誤處理:異常捕捉和恢復(fù)機(jī)制

    Golang錯(cuò)誤處理:異常捕捉和恢復(fù)機(jī)制

    Golang中,異常處理是通過 defer + panic + recover 的方式來實(shí)現(xiàn)的,使用 defer 可以將清理操作注冊(cè)到函數(shù)執(zhí)行完畢后執(zhí)行,而 panic 和 recover 可以用于處理異常,通過組合使用這些功能,可以實(shí)現(xiàn)更加健壯的程序
    2024-01-01
  • golang實(shí)現(xiàn)http server提供文件下載功能

    golang實(shí)現(xiàn)http server提供文件下載功能

    這篇文章主要介紹了golang實(shí)現(xiàn)http server提供文件下載功能,本文給大家簡單介紹了Golang的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 深入理解Golang中的dig包管理和解決依賴關(guān)系

    深入理解Golang中的dig包管理和解決依賴關(guān)系

    這篇文章主要為大家詳細(xì)介紹了golang中dig包的使用方法,探討其應(yīng)用場(chǎng)景,并提供一些示例,展示如何結(jié)合其他庫來更好地實(shí)現(xiàn)這些場(chǎng)景,感興趣的小伙伴可以了解下
    2024-01-01
  • golang中make和new的區(qū)別示例詳解

    golang中make和new的區(qū)別示例詳解

    Go 語言中的 new 和 make 一直是新手比較容易混淆的東西,咋一看很相似。不過解釋兩者之間的不同也非常容易,下面這篇文章主要介紹了golang中make和new的區(qū)別,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-08-08
  • Go語言提升開發(fā)效率的語法糖技巧分享

    Go語言提升開發(fā)效率的語法糖技巧分享

    每門語言都有自己的語法糖,像java的語法糖就有方法變長參數(shù)、拆箱與裝箱、枚舉、for-each等等,Go語言也不例外。本文就來介紹一些Go語言的語法糖,需要的可以參考一下
    2022-07-07

最新評(píng)論