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

Go中并發(fā)控制的實(shí)現(xiàn)方式總結(jié)

 更新時(shí)間:2023年12月21日 09:15:23   作者:Serena  
在Go實(shí)際開發(fā)中,并發(fā)安全是老生常談的事情,在并發(fā)下,goroutine之間的存在數(shù)據(jù)資源等方面的競(jìng)爭(zhēng),為了保證數(shù)據(jù)一致性、防止死鎖等問(wèn)題的出現(xiàn),在并發(fā)中需要使用一些方式來(lái)實(shí)現(xiàn)并發(fā)控制,本文給大家總結(jié)了幾種實(shí)現(xiàn)方式,需要的朋友可以參考下

Go的并發(fā)控制

在Go實(shí)際開發(fā)中,并發(fā)安全是老生常談的事情,在并發(fā)下,goroutine之間的存在數(shù)據(jù)資源等方面的競(jìng)爭(zhēng)。

為了保證數(shù)據(jù)一致性、防止死鎖等問(wèn)題的出現(xiàn),在并發(fā)中需要使用一些方式來(lái)實(shí)現(xiàn)并發(fā)控制。

并發(fā)控制的目的是確保在多個(gè)并發(fā)執(zhí)行的線程或進(jìn)程中,對(duì)共享資源的訪問(wèn)和操作能夠正確、有效地進(jìn)行,并且避免出現(xiàn)競(jìng)態(tài)條件和數(shù)據(jù)不一致的問(wèn)題。

在Go中,可以通過(guò)以下幾種方式來(lái)實(shí)現(xiàn)并發(fā)控制:

1、channel

channel通道主要用于于goroutine之間通信和同步的機(jī)制。通過(guò)使用channel,可以在不同的goroutine之間進(jìn)行數(shù)據(jù)的發(fā)送與接收,從而實(shí)現(xiàn)協(xié)調(diào)和控制并發(fā),以達(dá)到并發(fā)控制。

根據(jù)channel的類型,可以實(shí)現(xiàn)不同的并發(fā)控制效果:

無(wú)緩沖channel

當(dāng)使用make初始化時(shí),不指定channel的容量大小,即初始化無(wú)緩沖channel;

當(dāng)發(fā)送方向無(wú)緩沖channel發(fā)送消息數(shù)據(jù)時(shí),如果發(fā)送后channel的數(shù)據(jù)未被接收方獲取,則當(dāng)前goroutine會(huì)阻塞在發(fā)送語(yǔ)句中,直到有接收者準(zhǔn)備好接收數(shù)據(jù)為止,即無(wú)緩沖通道要求發(fā)送操作和接收操作同時(shí)準(zhǔn)備好才能完成通信。這樣做是確保了發(fā)送和接收的同步,避免了數(shù)據(jù)競(jìng)爭(zhēng)和不確定性。

package main

import (
    "fmt"
    "time"
)

func main() {
    // 創(chuàng)建一個(gè)無(wú)緩沖通道
    ch := make(chan int)

    // 啟動(dòng)一個(gè) goroutine 接收數(shù)據(jù)
    go func() {
       time.Sleep(time.Second * 5)
       fmt.Println("等待接收數(shù)據(jù)")
       data := <-ch // 接收數(shù)據(jù)
       fmt.Println("接收到數(shù)據(jù):", data)
    }()

    fmt.Println("發(fā)送數(shù)據(jù)")
    // 發(fā)送數(shù)據(jù),由于匿名函數(shù)goroutine睡眠,無(wú)緩沖通道內(nèi)數(shù)據(jù)沒(méi)有g(shù)oroutine接收,因此會(huì)阻塞。5s后被接收則繼續(xù)執(zhí)行
    ch <- 100 
    time.Sleep(time.Second)
    fmt.Println("程序結(jié)束")
}
  • 在上述代碼中,創(chuàng)建了一個(gè)無(wú)緩沖通道 ch。然后在一個(gè)單獨(dú)的 goroutine 中啟動(dòng)了一個(gè)接收操作,等待從通道 ch 中接收數(shù)據(jù)。

  • 接下來(lái),在main goroutine中執(zhí)行發(fā)送操作,向通道 ch 發(fā)送數(shù)據(jù) 100。

  • 由于無(wú)緩沖通道的特性,當(dāng)發(fā)送語(yǔ)句 ch <- 100 執(zhí)行時(shí),由于沒(méi)有接收者準(zhǔn)備好接收數(shù)據(jù)(單獨(dú)的goroutine處于5s睡眠),發(fā)送操作會(huì)被阻塞。

  • 接收方的 goroutine 在接收數(shù)據(jù)之前會(huì)一直等待。

  • 當(dāng)接收方的 goroutine 準(zhǔn)備好之后,發(fā)送操作完成,數(shù)據(jù)被成功發(fā)送并被接收方接收,然后程序繼續(xù)執(zhí)行后續(xù)語(yǔ)句,打印出相應(yīng)的輸出。

需要注意的是,在使用無(wú)緩沖channel時(shí),如果沒(méi)有接收者,發(fā)送操作將會(huì)永久阻塞,可能會(huì)導(dǎo)致死鎖,因此在使用無(wú)緩沖通道時(shí),需要確保發(fā)送和接收操作能夠匹配。

有緩沖channel

當(dāng)使用make初始化時(shí),可以指定channel的容量大小,即初始化有緩沖channel,通道的容量表示通道中最大能存放的元素?cái)?shù)量。

  • 當(dāng)發(fā)送方發(fā)送數(shù)據(jù)到有緩存channel時(shí),如果緩沖區(qū)滿了,則發(fā)送方會(huì)被阻塞直到有緩沖空間可以接收這個(gè)消息數(shù)據(jù);

  • 當(dāng)接收方在有緩沖channel接收數(shù)據(jù)時(shí),如果緩沖區(qū)為空,則接收方會(huì)被阻塞直到channel有數(shù)據(jù)可讀;

無(wú)論是緩存 channel 還是無(wú)緩沖 channel,都是并發(fā)安全的,即多個(gè) goroutine 可以同時(shí)發(fā)送和接收數(shù)據(jù),而不需要額外的同步機(jī)制。

但是,由于緩存 channel 具有緩存空間,因此在使用時(shí)需要特別注意緩存空間的大小,避免過(guò)度消耗內(nèi)存或者發(fā)生死鎖等問(wèn)題。

2、sync.WaitGroup

sync包中,sync.WaitGroup可以在并發(fā)goroutine之間起到執(zhí)行屏障的效果。WaitGroup提供了用于創(chuàng)建多個(gè)goroutine時(shí),能夠等待多個(gè)并發(fā)執(zhí)行的代碼塊在達(dá)到WaitGroup顯示指定的同步條件后,才可以繼續(xù)執(zhí)行Wait的后續(xù)代碼。在使用sync.WaitGroup實(shí)現(xiàn)同步模式下,從而起到并發(fā)控制的效果。

在Go中,sync.WaitGroup類型提供了如下幾個(gè)方法:

方法名功能說(shuō)明
func (wg * WaitGroup) Add(delta int)等待組計(jì)數(shù)器 + delta
(wg *WaitGroup) Done()等待組計(jì)數(shù)器-1
(wg *WaitGroup) Wait()阻塞直到等待組計(jì)數(shù)器變?yōu)?

示例:

package main

import (
    "fmt"
    "sync"
)

// 聲明全局等待組變量
var wg sync.WaitGroup

func printHello() {
    fmt.Println("Hello World")
    wg.Done() // 完成一個(gè)任務(wù)后,調(diào)用Done()方法,等待組減1,告知當(dāng)前goroutine已經(jīng)完成任務(wù)
}

func main() {
    wg.Add(1) // 等待組加1,表示登記一個(gè)goroutine
    go printHello()
    fmt.Println("main")
    wg.Wait() // 阻塞當(dāng)前goroutine,直到等待組中的所有g(shù)oroutine都完成任務(wù)
}

// 執(zhí)行結(jié)果
main
Hello World

3、sync.Mutex

sync.Mutex 是 Go 語(yǔ)言中的一個(gè)互斥鎖(Mutex)類型,用于實(shí)現(xiàn)對(duì)共享資源的互斥訪問(wèn)。

互斥鎖是一種常見的并發(fā)控制機(jī)制,它能夠確保在同一時(shí)刻只有一個(gè) goroutine 可以訪問(wèn)被保護(hù)的資源,從而避免數(shù)據(jù)競(jìng)爭(zhēng)和不確定的結(jié)果。

互斥鎖的作用可以有以下幾個(gè)方面:

  • 保護(hù)共享資源:當(dāng)多個(gè) goroutine并發(fā)訪問(wèn)共享資源時(shí),通過(guò)使用互斥鎖可以限制只有一個(gè) goroutine 可以訪問(wèn)共享資源,從而避免競(jìng)態(tài)條件和數(shù)據(jù)不一致的問(wèn)題。
  • 實(shí)現(xiàn)臨界區(qū):互斥鎖可以將一段代碼標(biāo)記為臨界區(qū),只有獲取了鎖的 goroutine 才能執(zhí)行該臨界區(qū)的代碼,其他 goroutine 則需要等待解鎖,才能夠訪問(wèn)臨界區(qū)內(nèi)的代碼塊。

互斥鎖的基本使用方式是,通過(guò)調(diào)用 Lock() 方法獲取鎖,執(zhí)行臨界區(qū)代碼,然后調(diào)用 Unlock() 方法釋放鎖。在獲取鎖之后,其他 goroutine 將會(huì)被阻塞,直到當(dāng)前 goroutine 釋放鎖為止。Lock() 方法與Unlock() 底層的實(shí)現(xiàn)原理是使用原子操作來(lái)維護(hù)Mutexstate狀態(tài)。

sync.Mutex中,除了最基本的互斥鎖外,還提供讀寫鎖,在讀多寫少的場(chǎng)景下,相比互斥鎖性能上能夠有所提升。

channel 與 Mutex 對(duì)比例子

在自增操作x++中,該操作并非原子操作,因此在多個(gè)goroutine對(duì)全局變量x進(jìn)行自增時(shí),會(huì)出現(xiàn)數(shù)據(jù)覆蓋的情況,因此可以通過(guò)一些方法來(lái)實(shí)現(xiàn)并發(fā)控制,例如channel、互斥鎖、原子操作。

可以對(duì)比一下channel互斥鎖在實(shí)現(xiàn)并發(fā)控制時(shí)的執(zhí)行時(shí)間:

  • 使用channel
package main

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

var x int64
var wg sync.WaitGroup

func main() {
    startTime := time.Now()
    ch := make(chan struct{}, 1)

    for i := 0; i < 10000; i++ {
       wg.Add(1)
       go func() {
          defer wg.Done()
          ch <- struct{}{}
          x++
          <-ch
       }()
    }
    wg.Wait()
    endTime := time.Now()
    fmt.Println(x)                      // 10000
    fmt.Println(endTime.Sub(startTime)) // 6.2933ms
}
  • 使用Mutex
package main

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

var x int64
var wg sync.WaitGroup
var lock sync.Mutex

func main() {
    startTime := time.Now()
    for i := 0; i < 10000; i++ {
       wg.Add(1)
       go func() {
          defer wg.Done()
          lock.Lock()
          x++
          lock.Unlock()
       }()
    }
    wg.Wait()
    endTime := time.Now()
    fmt.Println(x)                      // 10000
    fmt.Println(endTime.Sub(startTime)) // 3.0835ms
}

可以對(duì)比兩種方法的執(zhí)行時(shí)間,在啟動(dòng)10000個(gè)goroutine執(zhí)行10000次全局變量x++時(shí),channel實(shí)現(xiàn)并發(fā)控制全局變量x++的執(zhí)行時(shí)間為6.2933ms(存在波動(dòng)),而使用Mutex提供的互斥鎖實(shí)現(xiàn)并發(fā)控制全局變量x++的執(zhí)行時(shí)間為3.0835ms(存在波動(dòng)),大約在兩倍左右,這是為什么呢?

原因在于channel的操作涉及到**goroutine之間的調(diào)度和上下文的切換**,而互斥鎖底層使用了Go的原子操作,執(zhí)行時(shí)間較短,因?yàn)榛コ怄i的操作相對(duì)輕量,不涉及goroutine的調(diào)度以及上下文的切換。

在開發(fā)過(guò)程中,選擇使用通道還是互斥鎖取決于具體的場(chǎng)景與需求,并不是一定說(shuō)使用鎖就好,需要根據(jù)實(shí)際的業(yè)務(wù)場(chǎng)景來(lái)進(jìn)行選擇。如果需要更細(xì)粒度的控制和更高的并發(fā)性能,可以優(yōu)先考慮使用互斥鎖。

4、atomic原子操作

Go語(yǔ)言提供了原子操作用于對(duì)內(nèi)存中的變量進(jìn)行同步訪問(wèn),避免了多個(gè)goroutine同時(shí)訪問(wèn)同一個(gè)變量時(shí)可能產(chǎn)生的競(jìng)態(tài)條件。

sync/atomic包提供了原子加操作、比較并交換等方法提供一系列原子操作,這些方法利用底層的原子指令,確保對(duì)內(nèi)存中的變量進(jìn)行原子級(jí)別的訪問(wèn)和修改,從而實(shí)現(xiàn)并發(fā)控制。

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

var x int64
var wg sync.WaitGroup

// 使用原子操作
func atomicAdd() {
    atomic.AddInt64(&x, 1)
    wg.Done()
}

func main() {
    for i := 0; i < 10000; i++ {
       wg.Add(1)
       go atomicAdd() // 原子操作add函數(shù)
    }
    wg.Wait()
    fmt.Println(x) // 10000
}

一些常用的原子操作函數(shù):

  • Add函數(shù):AddInt32AddInt64、AddUint32、AddUint64等方法,用于對(duì)變量進(jìn)行原子加操作。
  • CompareAndSwap函數(shù):CompareAndSwapInt32、CompareAndSwapInt64、CompareAndSwapUint32、CompareAndSwapUint64等,用于比較并交換操作,當(dāng)舊值等于給定值時(shí),將新值賦值到指定地址中。
  • Load函數(shù):LoadInt32LoadInt64、LoadUint32LoadUint64等,用于加載操作,返回指定地址中存儲(chǔ)的值。
  • Store函數(shù):StoreInt32、StoreInt64StoreUint32、StoreUint64等,用于存儲(chǔ)操作,將給定的值存儲(chǔ)到指定地址中。
  • Swap函數(shù):SwapInt32、SwapInt64、SwapUint32SwapUint64等,用于交換操作,將指定地址中存儲(chǔ)的值和給定的值進(jìn)行交換,并返回原值。

以上就是Go中并發(fā)控制的實(shí)現(xiàn)方式總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Go并發(fā)控制實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • go語(yǔ)言如何使用gin庫(kù)實(shí)現(xiàn)SSE長(zhǎng)連接

    go語(yǔ)言如何使用gin庫(kù)實(shí)現(xiàn)SSE長(zhǎng)連接

    所謂長(zhǎng)連接指在一個(gè)TCP連接上可以連續(xù)發(fā)送多個(gè)數(shù)據(jù)包,在TCP連接保持期間,如果沒(méi)有數(shù)據(jù)包發(fā)送,需要雙方發(fā)檢測(cè)包以維持此連接,一般需要自己做在線維持,下面這篇文章主要給大家介紹了關(guān)于go語(yǔ)言如何使用gin庫(kù)實(shí)現(xiàn)SSE長(zhǎng)連接的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • go code review 代碼調(diào)試

    go code review 代碼調(diào)試

    這篇文章主要為大家介紹了go code review 代碼調(diào)試方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • GoLang中拼接字符串性能優(yōu)化方法詳解

    GoLang中拼接字符串性能優(yōu)化方法詳解

    最近在做性能優(yōu)化,有個(gè)函數(shù)里面的耗時(shí)特別長(zhǎng),看里面的操作大多是一些字符串拼接的操作,而字符串拼接在 golang 里面其實(shí)有很多種實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于Golang語(yǔ)言如何高效拼接字符串的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • Go 1.22對(duì)net/http包的路由增強(qiáng)功能詳解

    Go 1.22對(duì)net/http包的路由增強(qiáng)功能詳解

    Go 1.22 版本對(duì) net/http 包的路由功能進(jìn)行了增強(qiáng),引入了方法匹配(method matching)和通配符(wildcards)兩項(xiàng)新功能,本文將給大家詳細(xì)的介紹一下Go 1.22對(duì)net/http包的路由增強(qiáng)功能,需要的朋友可以參考下
    2024-02-02
  • 使用Gin框架返回JSON、XML和HTML數(shù)據(jù)

    使用Gin框架返回JSON、XML和HTML數(shù)據(jù)

    Gin是一個(gè)高性能的Go語(yǔ)言Web框架,它不僅提供了簡(jiǎn)潔的API,還支持快速的路由和中間件處理,在Web開發(fā)中,返回JSON、XML和HTML數(shù)據(jù)是非常常見的需求,本文將介紹如何使用Gin框架來(lái)返回這三種類型的數(shù)據(jù),需要的朋友可以參考下
    2024-08-08
  • Go設(shè)計(jì)模式之備忘錄模式講解和代碼示例

    Go設(shè)計(jì)模式之備忘錄模式講解和代碼示例

    備忘錄是一種行為設(shè)計(jì)模式, 允許生成對(duì)象狀態(tài)的快照并在以后將其還原,本文就通過(guò)代碼示例給大家講講Go備忘錄模式,感興趣的小伙伴跟著小編一起來(lái)看看吧
    2023-08-08
  • Gin與Mysql實(shí)現(xiàn)簡(jiǎn)單Restful風(fēng)格API實(shí)戰(zhàn)示例詳解

    Gin與Mysql實(shí)現(xiàn)簡(jiǎn)單Restful風(fēng)格API實(shí)戰(zhàn)示例詳解

    這篇文章主要為大家介紹了Gin與Mysql實(shí)現(xiàn)簡(jiǎn)單Restful風(fēng)格API示例詳解,有需要的朋友可以借鑒參考下希望能夠有所幫助,祝大家多多進(jìn)步
    2021-11-11
  • golang替換無(wú)法顯示的特殊字符(\u0000,?\000,?^@)

    golang替換無(wú)法顯示的特殊字符(\u0000,?\000,?^@)

    這篇文章主要介紹了golang替換無(wú)法顯示的特殊字符,包括的字符有\(zhòng)u0000,?\000,?^@等,下文詳細(xì)資料,需要的小伙伴可以參考一下
    2022-04-04
  • golang 函數(shù)返回chan類型的操作

    golang 函數(shù)返回chan類型的操作

    這篇文章主要介紹了golang 函數(shù)返回chan類型的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • 深入解析golang?bufio

    深入解析golang?bufio

    這篇文章主要介紹了golang?bufio解析,golang的bufio庫(kù)使用緩存來(lái)一次性進(jìn)行大塊數(shù)據(jù)的讀寫,以此降低IO系統(tǒng)調(diào)用,提升性能,需要的朋友可以參考下
    2022-04-04

最新評(píng)論