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

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

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

Go語(yǔ)言中的Sync鎖

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

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

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

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

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

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

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

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

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

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

一、互斥鎖(Mutex)

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

1.1 基本用法

package main

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

// 定義全局變量 票庫(kù)存為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 {
            // 票賣(mài)完,解鎖
            mutex.Unlock()
            fmt.Println("票已售完")
            break
        }
        // 操作完畢后,解鎖
        mutex.Unlock()
    }
}

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

在這里插入圖片描述

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

sync.WaitGroup類(lèi)型可以用來(lái)等待一組Goroutine完成。例如:

package main

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

// waitgroup、

var wg sync.WaitGroup

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

    go test1()
    go test2()

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

    // 理想狀態(tài):所有協(xié)程執(zhí)行完畢之后,自動(dòng)停止。
    //如果每次都強(qiáng)制設(shè)置個(gè)等待時(shí)間。那么協(xié)程代碼也可能在這個(gè)時(shí)間內(nèi)還沒(mé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()來(lái)在Goroutine完成時(shí)通知WaitGroup  如果不用defer就得把該方法放在其他代碼之后
    for i := 0; i < 10; i++ {
        fmt.Println("test2--", i)
    }
}

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

在這里插入圖片描述

1.3 注意事項(xiàng)

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

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

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

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

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

二、讀寫(xiě)鎖(RWMutex)

讀寫(xiě)鎖(sync.RWMutex)允許多個(gè)goroutine同時(shí)讀取資源,但在寫(xiě)入時(shí)會(huì)阻塞所有其他讀和寫(xiě)的goroutine。讀寫(xiě)鎖可以提高讀多寫(xiě)少的場(chǎng)景下的并發(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

    // 寫(xiě)操作
    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è)讀寫(xiě)鎖rwMu。readData函數(shù)用于讀取data中的值,在讀取之前先獲取讀鎖,讀取完成后釋放讀鎖。

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

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

2.2 注意事項(xiàng)

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

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

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

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類(lèi)型的變量once。

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

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

3.2 注意事項(xiàng)

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

性能開(kāi)銷(xiāo):雖然sync.Once的性能開(kāi)銷(xiāo)很小,但在高性能要求的場(chǎng)景下,仍然需要注意其使用。

四、總結(jié)

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

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

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

相關(guān)文章

  • Golang map如何生成有序的json數(shù)據(jù)詳解

    Golang map如何生成有序的json數(shù)據(jù)詳解

    最近在學(xué)習(xí)Golang,發(fā)現(xiàn)了一個(gè)問(wèn)題,覺(jué)著有必要給大家總結(jié)下,下面這篇文章主要給大家介紹了關(guān)于Golang map如何生成有序json數(shù)據(jù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面來(lái)一起看看吧。
    2017-07-07
  • Go各時(shí)間字符串使用解析

    Go各時(shí)間字符串使用解析

    這篇文章主要介紹了Go各時(shí)間字符串使用解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 基于Golang實(shí)現(xiàn)統(tǒng)一加載資源的入口

    基于Golang實(shí)現(xiàn)統(tǒng)一加載資源的入口

    當(dāng)我們需要在?main?函數(shù)中做一些初始化的工作,比如初始化日志,初始化配置文件,都需要統(tǒng)一初始化入口函數(shù),所以本文就來(lái)編寫(xiě)一個(gè)統(tǒng)一加載資源的入口吧
    2023-05-05
  • Go語(yǔ)言中 Channel 詳解

    Go語(yǔ)言中 Channel 詳解

    Go 語(yǔ)言中的 channel 是實(shí)現(xiàn) goroutine 間無(wú)鎖通信的關(guān)鍵機(jī)制,他使得寫(xiě)多線程并發(fā)程序變得簡(jiǎn)單、靈活、觸手可得。下面就個(gè)人理解對(duì) channel 使用過(guò)程中應(yīng)該注意的地方進(jìn)行一個(gè)簡(jiǎn)要的總結(jié)。
    2018-10-10
  • Golang中slice刪除元素的性能對(duì)比

    Golang中slice刪除元素的性能對(duì)比

    go沒(méi)有對(duì)刪除切片元素提供專(zhuān)用的語(yǔ)法或者接口,需要使用切片本身的特性來(lái)刪除元素,下面這篇文章主要給大家介紹了關(guān)于Golang中slice刪除元素的性能對(duì)比,需要的朋友可以參考下
    2022-06-06
  • Go語(yǔ)言高效編程的3個(gè)技巧總結(jié)

    Go語(yǔ)言高效編程的3個(gè)技巧總結(jié)

    Go語(yǔ)言是一種開(kāi)源編程語(yǔ)言,可輕松構(gòu)建簡(jiǎn)單、可靠且高效的軟件,下面這篇文章主要給大家分享介紹了關(guān)于Go語(yǔ)言高效編程的3個(gè)技巧,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • 一文帶你了解Go語(yǔ)言中接口的使用

    一文帶你了解Go語(yǔ)言中接口的使用

    這篇文章主要和大家分享一下Go語(yǔ)言中的接口的使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定的幫助,需要的小伙伴可以參考一下
    2022-12-12
  • golang-gin-mgo高并發(fā)服務(wù)器搭建教程

    golang-gin-mgo高并發(fā)服務(wù)器搭建教程

    這篇文章主要介紹了golang-gin-mgo高并發(fā)服務(wù)器搭建教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法

    gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法

    今天小編就為大家分享一篇gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-07-07
  • 使用Go語(yǔ)言實(shí)現(xiàn)跨域資源共享(CORS)設(shè)置

    使用Go語(yǔ)言實(shí)現(xiàn)跨域資源共享(CORS)設(shè)置

    在Web開(kāi)發(fā)中,跨域資源共享(CORS)是一種重要的安全機(jī)制,它允許許多資源在一個(gè)網(wǎng)頁(yè)上被另一個(gè)來(lái)源的網(wǎng)頁(yè)所訪問(wèn),然而,出于安全考慮,瀏覽器默認(rèn)禁止這種跨域訪問(wèn),為了解決這個(gè)問(wèn)題,我們可以使用Go語(yǔ)言來(lái)設(shè)置CORS,需要的朋友可以參考下
    2024-06-06

最新評(píng)論