亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go語(yǔ)言sync包與鎖實(shí)現(xiàn)限制線(xiàn)程對(duì)變量的訪(fǎng)問(wèn)

 更新時(shí)間:2023年04月13日 10:40:32   作者:C語(yǔ)言中文網(wǎng)  
本文主要介紹了Go語(yǔ)言sync包與鎖實(shí)現(xiàn)限制線(xiàn)程對(duì)變量的訪(fǎng)問(wèn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

Go語(yǔ)言中 sync 包里提供了互斥鎖 Mutex 和讀寫(xiě)鎖 RWMutex 用于處理并發(fā)過(guò)程中可能出現(xiàn)同時(shí)兩個(gè)或多個(gè)協(xié)程(或線(xiàn)程)讀或?qū)懲粋€(gè)變量的情況。

為什么需要鎖

鎖是 sync 包中的核心,它主要有兩個(gè)方法,分別是加鎖(Lock)和解鎖(Unlock)。

在并發(fā)的情況下,多個(gè)線(xiàn)程或協(xié)程同時(shí)其修改一個(gè)變量,使用鎖能保證在某一時(shí)間內(nèi),只有一個(gè)協(xié)程或線(xiàn)程修改這一變量。

不使用鎖時(shí),在并發(fā)的情況下可能無(wú)法得到想要的結(jié)果,如下所示:

package main
import (
    "fmt"
    "time"
)
func main() {
    var a = 0
    for i := 0; i < 1000; i++ {
        go func(idx int) {
            a += 1
            fmt.Println(a)
        }(i)
    }
    time.Sleep(time.Second)
}

從理論上來(lái)說(shuō),上面的程序會(huì)將 a 的值依次遞增輸出,然而實(shí)際結(jié)果卻是下面這樣子的。

537
995
996
997
538
999
1000

通過(guò)運(yùn)行結(jié)果可以看出 a 的值并不是按順序遞增輸出的,這是為什么呢?

協(xié)程的執(zhí)行順序大致如下所示:

  • 從寄存器讀取 a 的值;
  • 然后做加法運(yùn)算;
  • 最后寫(xiě)到寄存器。

按照上面的順序,假如有一個(gè)協(xié)程取得 a 的值為 3,然后執(zhí)行加法運(yùn)算,此時(shí)又有一個(gè)協(xié)程對(duì) a 進(jìn)行取值,得到的值同樣是 3,最終兩個(gè)協(xié)程的返回結(jié)果是相同的。

而鎖的概念就是,當(dāng)一個(gè)協(xié)程正在處理 a 時(shí)將 a 鎖定,其它協(xié)程需要等待該協(xié)程處理完成并將 a 解鎖后才能再進(jìn)行操作,也就是說(shuō)同時(shí)處理 a 的協(xié)程只能有一個(gè),從而避免上面示例中的情況出現(xiàn)。 

互斥鎖 Mutex

上面的示例中出現(xiàn)的問(wèn)題怎么解決呢?加一個(gè)互斥鎖 Mutex 就可以了。那什么是互斥鎖呢 ?互斥鎖中其有兩個(gè)方法可以調(diào)用,如下所示:

func (m *Mutex) Lock()
func (m *Mutex) Unlock()

將上面的代碼略作修改,如下所示:

package main
import (
    "fmt"
    "sync"
    "time"
)
func main() {
    var a = 0
    var lock sync.Mutex
    for i := 0; i < 1000; i++ {
        go func(idx int) {
            lock.Lock()
            defer lock.Unlock()
            a += 1
            fmt.Printf("goroutine %d, a=%d\n", idx, a)
        }(i)
    }
    // 等待 1s 結(jié)束主程序
    // 確保所有協(xié)程執(zhí)行完
    time.Sleep(time.Second)
}

運(yùn)行結(jié)果如下:

goroutine 995, a=996
goroutine 996, a=997
goroutine 997, a=998
goroutine 998, a=999
goroutine 999, a=1000

需要注意的是一個(gè)互斥鎖只能同時(shí)被一個(gè) goroutine 鎖定,其它 goroutine 將阻塞直到互斥鎖被解鎖(重新?tīng)?zhēng)搶對(duì)互斥鎖的鎖定),示例代碼如下:

package main
import (
    "fmt"
    "sync"
    "time"
)
func main() {
    ch := make(chan struct{}, 2)
    var l sync.Mutex
    go func() {
        l.Lock()
        defer l.Unlock()
        fmt.Println("goroutine1: 我會(huì)鎖定大概 2s")
        time.Sleep(time.Second * 2)
        fmt.Println("goroutine1: 我解鎖了,你們?nèi)尠?)
        ch <- struct{}{}
    }()
    go func() {
        fmt.Println("goroutine2: 等待解鎖")
        l.Lock()
        defer l.Unlock()
        fmt.Println("goroutine2: 歐耶,我也解鎖了")
        ch <- struct{}{}
    }()
    // 等待 goroutine 執(zhí)行結(jié)束
    for i := 0; i < 2; i++ {
        <-ch
    }
}

上面的代碼運(yùn)行結(jié)果如下:

goroutine1: 我會(huì)鎖定大概 2s
goroutine2: 等待解鎖
goroutine1: 我解鎖了,你們?nèi)尠?br />goroutine2: 歐耶,我也解鎖了

讀寫(xiě)鎖

讀寫(xiě)鎖有如下四個(gè)方法:

  • 寫(xiě)操作的鎖定和解鎖分別是func (*RWMutex) Lock和func (*RWMutex) Unlock;
  • 讀操作的鎖定和解鎖分別是func (*RWMutex) Rlock和func (*RWMutex) RUnlock。

讀寫(xiě)鎖的區(qū)別在于:

  • 當(dāng)有一個(gè) goroutine 獲得寫(xiě)鎖定,其它無(wú)論是讀鎖定還是寫(xiě)鎖定都將阻塞直到寫(xiě)解鎖;
  • 當(dāng)有一個(gè) goroutine 獲得讀鎖定,其它讀鎖定仍然可以繼續(xù);
  • 當(dāng)有一個(gè)或任意多個(gè)讀鎖定,寫(xiě)鎖定將等待所有讀鎖定解鎖之后才能夠進(jìn)行寫(xiě)鎖定。

所以說(shuō)這里的讀鎖定(RLock)目的其實(shí)是告訴寫(xiě)鎖定,有很多協(xié)程或者進(jìn)程正在讀取數(shù)據(jù),寫(xiě)操作需要等它們讀(讀解鎖)完才能進(jìn)行寫(xiě)(寫(xiě)鎖定)。

我們可以將其總結(jié)為如下三條:

  • 同時(shí)只能有一個(gè) goroutine 能夠獲得寫(xiě)鎖定;
  • 同時(shí)可以有任意多個(gè) gorouinte 獲得讀鎖定;
  • 同時(shí)只能存在寫(xiě)鎖定或讀鎖定(讀和寫(xiě)互斥)。

示例代碼如下所示:

package main

import (
? ? "fmt"
? ? "math/rand"
? ? "sync"
)
var count int
var rw sync.RWMutex
func main() {
? ? ch := make(chan struct{}, 10)
? ? for i := 0; i < 5; i++ {
? ? ? ? go read(i, ch)
? ? }
? ? for i := 0; i < 5; i++ {
? ? ? ? go write(i, ch)
? ? }
? ? for i := 0; i < 10; i++ {
? ? ? ? <-ch
? ? }
}
func read(n int, ch chan struct{}) {
? ? rw.RLock()
? ? fmt.Printf("goroutine %d 進(jìn)入讀操作...\n", n)
? ? v := count
? ? fmt.Printf("goroutine %d 讀取結(jié)束,值為:%d\n", n, v)
? ? rw.RUnlock()
? ? ch <- struct{}{}
}
func write(n int, ch chan struct{}) {
? ? rw.Lock()
? ? fmt.Printf("goroutine %d 進(jìn)入寫(xiě)操作...\n", n)
? ? v := rand.Intn(1000)
? ? count = v
? ? fmt.Printf("goroutine %d 寫(xiě)入結(jié)束,新值為:%d\n", n, v)
? ? rw.Unlock()
? ? ch <- struct{}{}
}

其執(zhí)行結(jié)果如下:

goroutine 0 進(jìn)入讀操作...
goroutine 0 讀取結(jié)束,值為:0
goroutine 3 進(jìn)入讀操作...
goroutine 1 進(jìn)入讀操作...
goroutine 3 讀取結(jié)束,值為:0
goroutine 1 讀取結(jié)束,值為:0
goroutine 4 進(jìn)入寫(xiě)操作...
goroutine 4 寫(xiě)入結(jié)束,新值為:81
goroutine 4 進(jìn)入讀操作...
goroutine 4 讀取結(jié)束,值為:81
goroutine 2 進(jìn)入讀操作...
goroutine 2 讀取結(jié)束,值為:81
goroutine 0 進(jìn)入寫(xiě)操作...
goroutine 0 寫(xiě)入結(jié)束,新值為:887
goroutine 1 進(jìn)入寫(xiě)操作...
goroutine 1 寫(xiě)入結(jié)束,新值為:847
goroutine 2 進(jìn)入寫(xiě)操作...
goroutine 2 寫(xiě)入結(jié)束,新值為:59
goroutine 3 進(jìn)入寫(xiě)操作...
goroutine 3 寫(xiě)入結(jié)束,新值為:81

下面再來(lái)看兩個(gè)示例。

【示例 1】多個(gè)讀操作同時(shí)讀取一個(gè)變量時(shí),雖然加了鎖,但是讀操作是不受影響的。(讀和寫(xiě)是互斥的,讀和讀不互斥)

package main
import (
    "sync"
    "time"
)
var m *sync.RWMutex
func main() {
    m = new(sync.RWMutex)
    // 多個(gè)同時(shí)讀
    go read(1)
    go read(2)
    time.Sleep(2*time.Second)
}
func read(i int) {
    println(i,"read start")
    m.RLock()
    println(i,"reading")
    time.Sleep(1*time.Second)
    m.RUnlock()
    println(i,"read over")
}

運(yùn)行結(jié)果如下:

1 read start
1 reading
2 read start
2 reading
1 read over
2 read over

【示例 2】由于讀寫(xiě)互斥,所以寫(xiě)操作開(kāi)始的時(shí)候,讀操作必須要等寫(xiě)操作進(jìn)行完才能繼續(xù),不然讀操作只能繼續(xù)等待。

package main
import (
    "sync"
    "time"
)
var m *sync.RWMutex
func main() {
    m = new(sync.RWMutex)
    // 寫(xiě)的時(shí)候啥也不能干
    go write(1)
    go read(2)
    go write(3)
    time.Sleep(2*time.Second)
}
func read(i int) {
    println(i,"read start")
    m.RLock()
    println(i,"reading")
    time.Sleep(1*time.Second)
    m.RUnlock()
    println(i,"read over")
}
func write(i int) {
    println(i,"write start")
    m.Lock()
    println(i,"writing")
    time.Sleep(1*time.Second)
    m.Unlock()
    println(i,"write over")
}

運(yùn)行結(jié)果如下:

1 write start
3 write start
1 writing
2 read start
1 write over
2 reading

到此這篇關(guān)于Go語(yǔ)言sync包與鎖實(shí)現(xiàn)限制線(xiàn)程對(duì)變量的訪(fǎng)問(wèn)的文章就介紹到這了,更多相關(guān)Go語(yǔ)言sync包與鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go slice切片使用示例詳解

    Go slice切片使用示例詳解

    這篇文章主要為大家介紹了Go slice切片使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Nunu快速構(gòu)建高效可靠Go應(yīng)用腳手架使用詳解

    Nunu快速構(gòu)建高效可靠Go應(yīng)用腳手架使用詳解

    這篇文章主要為大家介紹了如何使用Nunu快速構(gòu)建高效可靠Go應(yīng)用腳手架詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • mac下golang安裝了windows編譯環(huán)境后編譯變慢

    mac下golang安裝了windows編譯環(huán)境后編譯變慢

    這篇文章主要介紹了mac下golang安裝了windows編譯環(huán)境后編譯變慢的處理方法,非常的簡(jiǎn)單,有相同問(wèn)題的小伙伴可以參考下。
    2015-04-04
  • go學(xué)習(xí)筆記讀取consul配置文件詳解

    go學(xué)習(xí)筆記讀取consul配置文件詳解

    這篇文章主要為大家介紹了go學(xué)習(xí)筆記讀取consul配置文件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • 源碼剖析Golang中map擴(kuò)容底層的實(shí)現(xiàn)

    源碼剖析Golang中map擴(kuò)容底層的實(shí)現(xiàn)

    之前的文章詳細(xì)介紹過(guò)Go切片和map的基本使用,以及切片的擴(kuò)容機(jī)制。本文針對(duì)map的擴(kuò)容,會(huì)從源碼的角度全面的剖析一下map擴(kuò)容的底層實(shí)現(xiàn),需要的可以參考一下
    2023-03-03
  • golang程序進(jìn)度條實(shí)現(xiàn)示例詳解

    golang程序進(jìn)度條實(shí)現(xiàn)示例詳解

    這篇文章主要為大家介紹了golang程序?qū)崿F(xiàn)進(jìn)度條示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • go+redis實(shí)現(xiàn)消息隊(duì)列發(fā)布與訂閱的詳細(xì)過(guò)程

    go+redis實(shí)現(xiàn)消息隊(duì)列發(fā)布與訂閱的詳細(xì)過(guò)程

    這篇文章主要介紹了go+redis實(shí)現(xiàn)消息隊(duì)列發(fā)布與訂閱,redis做消息隊(duì)列的缺點(diǎn):沒(méi)有持久化,一旦消息沒(méi)有人消費(fèi),積累到一定程度后就會(huì)丟失,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • Golang標(biāo)準(zhǔn)庫(kù)container/list的用法圖文詳解

    Golang標(biāo)準(zhǔn)庫(kù)container/list的用法圖文詳解

    提到單向鏈表,大家應(yīng)該是比較熟悉的了,這篇文章主要為大家詳細(xì)介紹了Golang標(biāo)準(zhǔn)庫(kù)container/list的用法相關(guān)知識(shí),感興趣的小伙伴可以了解下
    2024-01-01
  • Golang微服務(wù)框架Kratos實(shí)現(xiàn)分布式任務(wù)隊(duì)列Asynq的方法詳解

    Golang微服務(wù)框架Kratos實(shí)現(xiàn)分布式任務(wù)隊(duì)列Asynq的方法詳解

    任務(wù)隊(duì)列(Task Queue) 一般用于跨線(xiàn)程或跨計(jì)算機(jī)分配工作的一種機(jī)制,在Golang語(yǔ)言里面,我們有像Asynq和Machinery這樣的類(lèi)似于Celery的分布式任務(wù)隊(duì)列,本文就給大家詳細(xì)介紹一下Golang微服務(wù)框架Kratos實(shí)現(xiàn)分布式任務(wù)隊(duì)列Asynq的方法,需要的朋友可以參考下
    2023-09-09
  • Golang處理內(nèi)存溢出方式

    Golang處理內(nèi)存溢出方式

    本文介紹了Golang中分析內(nèi)存溢出問(wèn)題的三種工具:pprof、GoMemstats和程序crash時(shí)自動(dòng)創(chuàng)建dump文件,通過(guò)這些工具,可以對(duì)程序的內(nèi)存使用情況進(jìn)行詳細(xì)分析,從而找出內(nèi)存溢出的原因
    2024-12-12

最新評(píng)論