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

Go多線程中數(shù)據(jù)不一致問題的解決方案(sync鎖機(jī)制)

 更新時(shí)間:2024年10月11日 11:01:13   作者:景天科技苑  
在Go語言的并發(fā)編程中,如何確保多個(gè)goroutine安全地訪問共享資源是一個(gè)關(guān)鍵問題,Go語言提供了sync包,其中包含了多種同步原語,用于解決并發(fā)編程中的同步問題,本文將詳細(xì)介紹sync包中的鎖機(jī)制,需要的朋友可以參考下

Go語言中的Sync鎖

在Go語言的并發(fā)編程中,如何確保多個(gè)goroutine安全地訪問共享資源是一個(gè)關(guān)鍵問題。Go語言提供了sync包,其中包含了多種同步原語,用于解決并發(fā)編程中的同步問題。本文將詳細(xì)介紹sync包中的鎖機(jī)制,并結(jié)合實(shí)際案例,幫助讀者理解和使用這些鎖。

要想解決臨界資源安全的問題,很多編程語言的解決方案都是同步。
通過上鎖的方式,某一時(shí)間段,只能允許一個(gè)goroutine來訪問這個(gè)共享數(shù)據(jù),當(dāng)前goroutine訪問完畢, 解鎖后,其他的goroutine才 能來訪問。

我們可以借助于sync包下的鎖操作。 synchronization

但是實(shí)際上,在Go的并發(fā)編程中有一句很經(jīng)典的話:不要以共享內(nèi)存的方式去通信:鎖,而要以通信的方式去共享內(nèi)存。

共享內(nèi)存的方式

鎖:多個(gè)線程拿的是同一個(gè)鑰匙,go語言不建議使用鎖機(jī)制來解決。不要以共享內(nèi)存的方式去通信

而要以通信的方式去共享內(nèi)存 go語言更建議我們使用 chan(通道) 來解決安全問題。(后面會(huì)學(xué))

在Go語言中并不鼓勵(lì)用鎖保護(hù)共享狀態(tài)的方式,在不同的Goroutine中分享信息(以共享內(nèi)存的方式去通信)。
而是鼓勵(lì)通過channeI將共享狀態(tài)或共享狀態(tài)的變化在各個(gè)Goroutine之間傳遞(以通信的方式去共享內(nèi)存),這樣同樣能像用鎖一樣保證在同一的時(shí)間只有一個(gè)Goroutine訪問共享狀態(tài)。

當(dāng)然,在主流的編程語言中為了保證多線程之間共享數(shù)據(jù)安全性和一致性,都會(huì)提供一套基本的同步工具集,如鎖,條件變量,原子操作等等。

Go語言標(biāo)準(zhǔn)庫也毫不意外的提供了這些同步機(jī)制,使用方式也和其他語言也差不多。

一、互斥鎖(Mutex)

互斥鎖(sync.Mutex)是最基本的同步機(jī)制之一,用于確保同一時(shí)間只有一個(gè)goroutine能夠訪問特定的資源。
當(dāng)一個(gè)goroutine持有互斥鎖時(shí),其他試圖獲取該鎖的goroutine將會(huì)被阻塞,直到鎖被釋放。

1.1 基本用法

package main

import (
    "fmt"
    "sync"
    "time"
)

// 定義全局變量 票庫存為10張
var tickets int = 10

// 定義一個(gè)鎖  Mutex 鎖頭
var mutex sync.Mutex

func main() {
    go saleTicket("張三")
    go saleTicket("李四")
    go saleTicket("王五")
    go saleTicket("趙六")

    time.Sleep(time.Second * 5)
}

// 售票函數(shù)
func saleTicket(name string) {
    for {
        // 在拿到共享資源之前先上鎖
        mutex.Lock()
        if tickets > 0 {
            time.Sleep(time.Millisecond * 1)
            fmt.Println(name, "剩余票的數(shù)量為:", tickets)
            tickets--
        } else {
            // 票賣完,解鎖
            mutex.Unlock()
            fmt.Println("票已售完")
            break
        }
        // 操作完畢后,解鎖
        mutex.Unlock()
    }
}

上鎖之后,就不會(huì)出現(xiàn)問題了

在這里插入圖片描述

1.2 使用sync.WaitGroup等待一組Goroutine完成

sync.WaitGroup類型可以用來等待一組Goroutine完成。例如:

package main

import (
    "fmt"
    "sync"
    "time"
)

// waitgroup、

var wg sync.WaitGroup

func main() {
    // 公司最后關(guān)門的人   0
    // wg.Add(2) wg.Add(2)來告訴WaitGroup我們要等待兩個(gè)Goroutine完成  開啟幾個(gè)協(xié)程,就add幾個(gè)
    // wg.Done() 我告知我已經(jīng)結(jié)束了  defer wg.Done()來在Goroutine完成時(shí)通知WaitGroup
    // 開啟幾個(gè)協(xié)程,就add幾個(gè)
    wg.Add(2)

    go test1()
    go test2()

    fmt.Println("main等待ing")
    wg.Wait() // 等待 wg 歸零,wg.Wait()來等待所有Goroutine完成 代碼才會(huì)繼續(xù)向下執(zhí)行
    fmt.Println("end")

    // 理想狀態(tài):所有協(xié)程執(zhí)行完畢之后,自動(dòng)停止。
    //如果每次都強(qiáng)制設(shè)置個(gè)等待時(shí)間。那么協(xié)程代碼也可能在這個(gè)時(shí)間內(nèi)還沒跑完,也可能提前就跑完了,所以設(shè)置死的等待時(shí)間不合理。此時(shí)就需要用到了等待組WaitGroup
    //time.Sleep(1 * time.Second)

}
func test1() {
    for i := 0; i < 10; i++ {
        time.Sleep(1 * time.Second)
        fmt.Println("test1--", i)
    }
    wg.Done() //這里就將該代碼塊放在了其他邏輯之后
}
func test2() {
    defer wg.Done() // defer wg.Done()來在Goroutine完成時(shí)通知WaitGroup  如果不用defer就得把該方法放在其他代碼之后
    for i := 0; i < 10; i++ {
        fmt.Println("test2--", i)
    }
}

主線程會(huì)等待所有協(xié)程執(zhí)行完畢,才繼續(xù)往下執(zhí)行代碼

在這里插入圖片描述

1.3 注意事項(xiàng)

避免死鎖:確保在獲取鎖之后,無論發(fā)生什么情況(包括panic),都能夠釋放鎖??梢允褂胐efer語句來確保鎖的釋放。

減少鎖的持有時(shí)間:鎖的持有時(shí)間越長,其他goroutine被阻塞的時(shí)間就越長,系統(tǒng)的并發(fā)性能就越差。因此,應(yīng)該盡量減少鎖的持有時(shí)間,只在必要的代碼段中持有鎖。

避免嵌套鎖:盡量避免在一個(gè)鎖已經(jīng)持有的情況下再嘗試獲取另一個(gè)鎖,這可能會(huì)導(dǎo)致死鎖。

避免忘記調(diào)用Done:如果忘記調(diào)用Done方法,WaitGroup將會(huì)永遠(yuǎn)等待下去,導(dǎo)致程序無法正常結(jié)束。

避免負(fù)數(shù)計(jì)數(shù)器:調(diào)用Add方法時(shí),如果傳入的參數(shù)為負(fù)數(shù),或者導(dǎo)致計(jì)數(shù)器變?yōu)樨?fù)數(shù),將會(huì)導(dǎo)致panic。

二、讀寫鎖(RWMutex)

讀寫鎖(sync.RWMutex)允許多個(gè)goroutine同時(shí)讀取資源,但在寫入時(shí)會(huì)阻塞所有其他讀和寫的goroutine。讀寫鎖可以提高讀多寫少的場景下的并發(fā)性能。

2.1 基本用法

package main

import (
    "fmt"
    "sync"
)

var (
    data map[string]int
    rwMu sync.RWMutex
)

func readData(key string) int {
    rwMu.RLock()
    defer rwMu.RUnlock()
    return data[key]
}

func writeData(key string, value int) {
    rwMu.Lock()
    defer rwMu.Unlock()
    data[key] = value
}

func main() {
    data = make(map[string]int)

    var wg sync.WaitGroup

    // 寫操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            writeData(fmt.Sprintf("key%d", i), i*10)
        }(i)
    }

    // 讀操作
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            value := readData(fmt.Sprintf("key%d", i%10))
            fmt.Printf("Read: key%d = %d\n", i%10, value)
        }(i)
    }

    wg.Wait()
}

在這里插入圖片描述

在上述代碼中,我們定義了一個(gè)全局變量data和一個(gè)讀寫鎖rwMu。readData函數(shù)用于讀取data中的值,在讀取之前先獲取讀鎖,讀取完成后釋放讀鎖。

writeData函數(shù)用于寫入data中的值,在寫入之前先獲取寫鎖,寫入完成后釋放寫鎖。

在main函數(shù)中,我們啟動(dòng)了10個(gè)寫goroutine和100個(gè)讀goroutine,分別調(diào)用writeData和readData函數(shù)。通過sync.WaitGroup等待所有g(shù)oroutine完成。

2.2 注意事項(xiàng)

避免寫鎖長時(shí)間持有:寫鎖會(huì)阻塞所有其他讀和寫的goroutine,因此應(yīng)該盡量減少寫鎖的持有時(shí)間。
讀多寫少場景:讀寫鎖適用于讀多寫少的場景,如果寫操作非常頻繁,讀寫鎖的性能優(yōu)勢可能會(huì)消失。
避免嵌套鎖:與互斥鎖類似,讀寫鎖也應(yīng)該避免嵌套使用。

三、Once(一次執(zhí)行)

sync.Once用于確保某個(gè)操作只執(zhí)行一次,無論有多少個(gè)goroutine調(diào)用它。這對(duì)于單例模式或初始化只執(zhí)行一次的場景非常有用。

3.1 基本用法

package main

import (
    "fmt"
    "sync"
)

var (
    once    sync.Once
    message string
)

func initMessage() {
    message = "Hello, World!"
}

func printMessage() {
    once.Do(initMessage)
    fmt.Println(message)
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            printMessage()
        }()
    }

    wg.Wait()
}

在這里插入圖片描述

在上述代碼中,我們定義了一個(gè)全局變量message和一個(gè)sync.Once類型的變量once。

initMessage函數(shù)用于初始化message的值。printMessage函數(shù)通過once.Do方法確保initMessage只被調(diào)用一次,然后打印出message的值。

在main函數(shù)中,我們啟動(dòng)了10個(gè)goroutine,每個(gè)goroutine都調(diào)用printMessage函數(shù)。通過sync.WaitGroup等待所有g(shù)oroutine完成。

3.2 注意事項(xiàng)

避免重復(fù)初始化:sync.Once確保某個(gè)操作只執(zhí)行一次,因此它通常用于初始化全局變量或執(zhí)行其他只需要執(zhí)行一次的操作。

性能開銷:雖然sync.Once的性能開銷很小,但在高性能要求的場景下,仍然需要注意其使用。

四、總結(jié)

本文詳細(xì)介紹了Go語言中sync包中的鎖機(jī)制,包括互斥鎖(sync.Mutex)、讀寫鎖(sync.RWMutex)、Once(一次執(zhí)行)和WaitGroup(等待組)。

通過實(shí)際案例,幫助讀者理解和使用這些鎖。在并發(fā)編程中,正確地使用這些同步原語,可以確保多個(gè)goroutine安全地訪問共享資源,避免數(shù)據(jù)競爭和其他并發(fā)問題。希望本文能夠?qū)Υ蠹矣兴鶐椭?/p>

以上就是Go多線程中數(shù)據(jù)不一致問題的解決方案的詳細(xì)內(nèi)容,更多關(guān)于Go多線程數(shù)據(jù)不一致的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang如何去除多余空白字符(含制表符)

    golang如何去除多余空白字符(含制表符)

    這篇文章主要介紹了golang去除多余空白字符(含制表符)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 淺析golang的依賴注入

    淺析golang的依賴注入

    這篇文章主要介紹了淺析golang的依賴注入,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析

    golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析

    這篇文章主要介紹了golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Go語言編譯原理之源碼調(diào)試

    Go語言編譯原理之源碼調(diào)試

    這篇文章主要為大家介紹了Go語言編譯原理之源碼調(diào)試示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例

    Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例

    這篇文章主要介紹了Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • 正則表達(dá)式在Go字符串處理中的實(shí)戰(zhàn)應(yīng)用代碼

    正則表達(dá)式在Go字符串處理中的實(shí)戰(zhàn)應(yīng)用代碼

    在Go語言中,正則表達(dá)式與字符串處理是強(qiáng)大的工具,能夠大幅提升文本處理效率,下面這篇文章主要介紹了正則表達(dá)式在Go字符串處理中實(shí)戰(zhàn)應(yīng)用的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2026-03-03
  • 深入理解Go設(shè)計(jì)模式之代理模式

    深入理解Go設(shè)計(jì)模式之代理模式

    代理模式是一種結(jié)構(gòu)型設(shè)計(jì)模式,?其中代理控制著對(duì)于原對(duì)象的訪問,?并允許在將請求提交給原對(duì)象的前后進(jìn)行一些處理,從而增強(qiáng)原對(duì)象的邏輯處理,這篇文章主要來學(xué)習(xí)一下代理模式的構(gòu)成和用法,需要的朋友可以參考下
    2023-05-05
  • Go語言中的數(shù)據(jù)格式(json、xml?、msgpack、protobuf)使用總結(jié)

    Go語言中的數(shù)據(jù)格式(json、xml?、msgpack、protobuf)使用總結(jié)

    在分布式的系統(tǒng)中,因?yàn)樯婕暗綌?shù)據(jù)的傳輸,所以一定會(huì)進(jìn)行數(shù)據(jù)的交換,此時(shí)就要定義數(shù)據(jù)交換的格式,例如二進(jìn)制、Json、Xml等等。本文總結(jié)了Go語言中的數(shù)據(jù)格式,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Go項(xiàng)目的目錄結(jié)構(gòu)詳解

    Go項(xiàng)目的目錄結(jié)構(gòu)詳解

    這篇文章主要介紹了Go項(xiàng)目的目錄結(jié)構(gòu),對(duì)基礎(chǔ)目錄做了講解,對(duì)項(xiàng)目開發(fā)中的其它目錄也一并做了介紹,需要的朋友可以參考下
    2014-10-10
  • Golang使用Zookeeper實(shí)現(xiàn)分布式鎖

    Golang使用Zookeeper實(shí)現(xiàn)分布式鎖

    分布式鎖是一種在分布式系統(tǒng)中用于控制并發(fā)訪問的機(jī)制,ZooKeeper?和?Redis?都是常用的實(shí)現(xiàn)分布式鎖的工具,本文就來使用Zookeeper實(shí)現(xiàn)分布式鎖,希望對(duì)大家有所幫助
    2024-02-02

最新評(píng)論

秦皇岛市| 新宁县| 三门峡市| 海城市| 景谷| 岑溪市| 九寨沟县| 抚顺县| 准格尔旗| 静宁县| 苗栗市| 罗城| 城固县| 濉溪县| 宣武区| 江门市| 鹿邑县| 灵璧县| 勐海县| 屏南县| 哈巴河县| 新乐市| 太和县| 黎平县| 眉山市| 客服| 中山市| 衡水市| 泰和县| 双峰县| 青海省| 榆树市| 当涂县| 方山县| 舞钢市| 泽普县| 调兵山市| 龙江县| 邯郸市| 商城县| 靖江市|