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

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

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

Go語言中的Sync鎖

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

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

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

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

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

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

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

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

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

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

一、互斥鎖(Mutex)

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

1.1 基本用法

package main

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

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

// 定義一個鎖  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()
    }
}

上鎖之后,就不會出現(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我們要等待兩個Goroutine完成  開啟幾個協(xié)程,就add幾個
    // wg.Done() 我告知我已經(jīng)結(jié)束了  defer wg.Done()來在Goroutine完成時通知WaitGroup
    // 開啟幾個協(xié)程,就add幾個
    wg.Add(2)

    go test1()
    go test2()

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

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

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

在這里插入圖片描述

1.3 注意事項

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

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

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

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

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

二、讀寫鎖(RWMutex)

讀寫鎖(sync.RWMutex)允許多個goroutine同時讀取資源,但在寫入時會阻塞所有其他讀和寫的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()
}

在這里插入圖片描述

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

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

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

2.2 注意事項

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

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

sync.Once用于確保某個操作只執(zhí)行一次,無論有多少個goroutine調(diào)用它。這對于單例模式或初始化只執(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()
}

在這里插入圖片描述

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

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

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

3.2 注意事項

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

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

四、總結(jié)

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

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

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

相關(guān)文章

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

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

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

    Go各時間字符串使用解析

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

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

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

    Go語言中 Channel 詳解

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

    Golang中slice刪除元素的性能對比

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

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

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

    一文帶你了解Go語言中接口的使用

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

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

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

    gorm golang 并發(fā)連接數(shù)據(jù)庫報錯的解決方法

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

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

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

最新評論