自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go并發(fā)編程詳解鎖、WaitGroup、Channel

開(kāi)發(fā) 前端
Channel 可以分為有緩沖和無(wú)緩沖兩種。無(wú)緩沖的 Channel 是同步的,有緩沖的 Channel 是異步的。發(fā)送操作只有在 Channel 滿時(shí)才會(huì)阻塞,接收操作只有在 Channel 為空時(shí)才會(huì)阻塞。

在傳統(tǒng)的編程語(yǔ)言中,如C++、Java、Python等,其并發(fā)邏輯多建立在操作系統(tǒng)線程之上。線程間的通信通常依賴于操作系統(tǒng)提供的基礎(chǔ)原語(yǔ),包括共享內(nèi)存、信號(hào)、管道、消息隊(duì)列及套接字等,其中共享內(nèi)存是最為普遍的通信方式。但這種基于共享內(nèi)存的并發(fā)模型在復(fù)雜或大規(guī)模業(yè)務(wù)場(chǎng)景下往往顯得復(fù)雜且易于出錯(cuò)。

Go語(yǔ)言在設(shè)計(jì)時(shí)即以解決傳統(tǒng)并發(fā)問(wèn)題為目標(biāo),融入了CSP(Communicating Sequential Processes,通信順序進(jìn)程)模型的理念。CSP模型致力于簡(jiǎn)化并發(fā)編程,目標(biāo)是讓編寫(xiě)并發(fā)程序的難度與順序程序相當(dāng)。

在CSP模型中,通信和同步通過(guò)一種特定的流程實(shí)現(xiàn):生產(chǎn)者產(chǎn)生數(shù)據(jù),然后通過(guò)輸出數(shù)據(jù)到輸入/輸出原語(yǔ),最終到達(dá)消費(fèi)者。Go語(yǔ)言為實(shí)現(xiàn)CSP模型,特別引入了Channel機(jī)制。Goroutine可以通過(guò)Channel進(jìn)行數(shù)據(jù)的讀寫(xiě)操作,Channel作為連接多個(gè)Goroutine的通信橋梁,簡(jiǎn)化了并發(fā)編程的復(fù)雜性。

雖然CSP模型在Go語(yǔ)言中占據(jù)主流地位,但Go同樣支持基于共享內(nèi)存的并發(fā)模型。在Go的sync包中,提供了包括互斥鎖、讀寫(xiě)鎖、條件變量和原子操作等在內(nèi)的多種同步機(jī)制,以滿足不同并發(fā)場(chǎng)景下的需求。

互斥鎖(Mutex)

基本概念

互斥鎖(Mutex)是一種用于在并發(fā)環(huán)境中安全訪問(wèn)共享資源的機(jī)制。當(dāng)一個(gè)協(xié)程獲取到鎖時(shí),它將擁有臨界區(qū)的訪問(wèn)權(quán),而其他請(qǐng)求該鎖的協(xié)程將會(huì)阻塞,直到該鎖被釋放。

應(yīng)用場(chǎng)景

并發(fā)訪問(wèn)共享資源的情形非常普遍,例如:

  • 秒殺系統(tǒng)
  • 多個(gè)goroutine并發(fā)修改某個(gè)變量
  • 同時(shí)更新用戶信息

如果沒(méi)有互斥鎖的控制,將會(huì)導(dǎo)致商品超賣(mài)、變量數(shù)值不正確、用戶信息更新錯(cuò)誤等問(wèn)題。這時(shí)候就需要使用互斥鎖來(lái)控制并發(fā)訪問(wèn)。

基本用法

Mutex實(shí)現(xiàn)了Locker接口,提供了兩個(gè)方法:LockUnlock

  • Lock方法用于對(duì)臨界區(qū)上鎖,獲得該鎖的協(xié)程擁有臨界資源的訪問(wèn)權(quán),其他請(qǐng)求臨界區(qū)的協(xié)程會(huì)阻塞等待該鎖的釋放。
  • Unlock方法用于解鎖,釋放鎖使其他協(xié)程可以訪問(wèn)臨界區(qū)。
package main

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

func main() {
        var mu sync.Mutex
        var count int

        increment := func() {
                mu.Lock()
                defer mu.Unlock()
                count++
                fmt.Println("Count:", count)
        }

        for i := 0; i < 5; i++ {
                go increment()
        }

        time.Sleep(time.Second)
}

易錯(cuò)場(chǎng)景

  • 不可重入的互斥鎖:Go的Mutex是不可重入鎖。由于Mutex鎖沒(méi)有記錄鎖的持有者信息,無(wú)法得知誰(shuí)擁有鎖。如果一個(gè)獲取了鎖的協(xié)程再次請(qǐng)求鎖,將會(huì)被阻塞,形成死鎖。
func example() {
        var mu sync.Mutex
        mu.Lock()
        defer mu.Unlock()
        // Do something...
        mu.Lock() // 死鎖
}
  • Lock和Unlock不配對(duì):未正確配對(duì)的Lock和Unlock調(diào)用會(huì)導(dǎo)致死鎖。如果對(duì)已經(jīng)鎖定的鎖再次調(diào)用Lock,將會(huì)阻塞;對(duì)未鎖定的Mutex調(diào)用Unlock將會(huì)panic。
func example() {
        var mu sync.Mutex
        mu.Lock()
        // 未調(diào)用mu.Unlock()
        mu.Unlock() // 正確
}
  • 復(fù)制已使用的鎖:復(fù)制已使用的鎖會(huì)導(dǎo)致意外的行為。
func example() {
        var mu sync.Mutex
        copyMu := mu
        copyMu.Lock() // 錯(cuò)誤
}

基本實(shí)現(xiàn)

Mutex結(jié)構(gòu)體有兩個(gè)字段:state和sema。

type Mutex struct {
        state int32
        sema  uint32
}

State字段的含義

Mutex有以下四種狀態(tài)。

  1. mutexLocked:Mutex上鎖標(biāo)志
  2. mutexWoken:Mutex喚醒標(biāo)志
  3. mutexStarving:Mutex正常/饑餓模式標(biāo)志
  4. waiterCount:等待者數(shù)量

Mutex的正常模式和饑餓模式

  • 正常模式:等待者隊(duì)列遵循先入先出原則,被喚醒的goroutine不會(huì)直接獲得鎖,而是與新請(qǐng)求鎖的goroutine競(jìng)爭(zhēng)鎖。新請(qǐng)求鎖的goroutine由于正在CPU上執(zhí)行,獲得鎖的幾率更大,從而減少上下文切換的性能損失。然而,這可能導(dǎo)致被喚醒的goroutine長(zhǎng)時(shí)間無(wú)法獲得鎖。
  • 饑餓模式:當(dāng)?shù)却龝r(shí)間超過(guò)閾值1毫秒時(shí),進(jìn)入饑餓模式。被喚醒的goroutine被放入等待隊(duì)列的隊(duì)首,當(dāng)前goroutine在調(diào)用Unlock釋放鎖時(shí),會(huì)直接將鎖交給等待隊(duì)列的隊(duì)首,新請(qǐng)求鎖的goroutine不會(huì)參與競(jìng)爭(zhēng),而是排到等待隊(duì)列的隊(duì)尾。當(dāng)?shù)却?duì)列沒(méi)有g(shù)oroutine或等待時(shí)間小于1毫秒時(shí),Mutex將從饑餓模式切換回正常模式。

代碼示例

以下代碼展示了如何使用Mutex在并發(fā)環(huán)境中安全地訪問(wèn)共享資源:

package main

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

func main() {
        var mu sync.Mutex
        var count int

        increment := func() {
                mu.Lock()
                defer mu.Unlock()
                count++
                fmt.Println("Count:", count)
        }

        for i := 0; i < 5; i++ {
                go increment()
        }

        time.Sleep(time.Second)
}

在上述代碼中,多個(gè)goroutine同時(shí)調(diào)用increment函數(shù),通過(guò)Mutex來(lái)確保對(duì)共享變量count的訪問(wèn)是安全的。

讀寫(xiě)鎖(RWMutex)

基本概念

在并發(fā)編程中,為了保證多個(gè)協(xié)程安全地訪問(wèn)共享資源,我們通常使用Mutex互斥鎖。然而,在讀多寫(xiě)少的場(chǎng)景下,Mutex會(huì)導(dǎo)致性能問(wèn)題,因?yàn)樗胁僮鳎òㄗx操作)都必須串行進(jìn)行。為了解決這一問(wèn)題,可以區(qū)分讀操作和寫(xiě)操作。RWMutex是一種讀寫(xiě)鎖,同一時(shí)間只能被一個(gè)寫(xiě)操作持有,或者被多個(gè)讀操作持有。

基本用法

RWMutex提供了五個(gè)方法:Lock、Unlock、RLock、RUnlock和RLocker。

  • Lock方法用于在寫(xiě)操作時(shí)獲取寫(xiě)鎖,會(huì)阻塞等待當(dāng)前未釋放的寫(xiě)鎖。當(dāng)處于寫(xiě)鎖狀態(tài)時(shí),新的讀操作將會(huì)阻塞等待。
  • Unlock方法用于釋放寫(xiě)鎖。
  • RLock方法用于在讀操作時(shí)獲取讀鎖,會(huì)阻塞等待當(dāng)前寫(xiě)鎖的釋放。如果鎖處于讀鎖狀態(tài),當(dāng)前協(xié)程也能獲取讀鎖。
  • RUnlock方法用于釋放讀鎖。
  • RLocker方法用于獲取一個(gè)Locker接口的對(duì)象,調(diào)用其Lock方法時(shí)會(huì)調(diào)用RLock方法,調(diào)用Unlock方法時(shí)會(huì)調(diào)用RUnlock方法。
package main

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

func main() {
        var rw sync.RWMutex
        var count int

        write := func() {
                rw.Lock()
                defer rw.Unlock()
                count++
                fmt.Println("Write:", count)
        }

        read := func() {
                rw.RLock()
                defer rw.RUnlock()
                fmt.Println("Read:", count)
        }

        // Start multiple readers
        for i := 0; i < 5; i++ {
                go read()
        }

        // Start a single writer
        go write()

        time.Sleep(time.Second)
}

實(shí)現(xiàn)原理

RWMutex主要通過(guò)readerCount字段來(lái)維護(hù)讀鎖的數(shù)量。寫(xiě)操作時(shí),會(huì)將readerCount減去2的30次方變成一個(gè)負(fù)數(shù),從而阻塞新的讀鎖請(qǐng)求。當(dāng)寫(xiě)鎖被釋放時(shí),將readerCount加上2的30次方,恢復(fù)成一個(gè)整數(shù)并喚醒等待中的讀鎖操作。

易錯(cuò)場(chǎng)景

RWMutex的易錯(cuò)場(chǎng)景和Mutex類似,包括以下幾點(diǎn)。

  • 不可重入鎖:Go的RWMutex是不可重入鎖。如果一個(gè)獲取了鎖的協(xié)程再次請(qǐng)求同一個(gè)鎖,將會(huì)被阻塞,形成死鎖。
func example() {
        var rw sync.RWMutex
        rw.Lock()
        defer rw.Unlock()
        // Do something...
        rw.Lock() // 死鎖
}
  • Lock和Unlock不配對(duì):未正確配對(duì)的Lock和Unlock調(diào)用會(huì)導(dǎo)致死鎖。如果對(duì)已經(jīng)鎖定的鎖再次調(diào)用Lock,將會(huì)阻塞;對(duì)未鎖定的RWMutex調(diào)用Unlock將會(huì)panic。
func example() {
        var rw sync.RWMutex
        rw.Lock()
        // 未調(diào)用rw.Unlock()
        rw.Unlock() // 正確
}
  • 復(fù)制已使用的鎖:復(fù)制已使用的鎖會(huì)導(dǎo)致意外行為。
func example() {
        var rw sync.RWMutex
        copyRw := rw
        copyRw.Lock() // 錯(cuò)誤
}
  • 隱蔽的死鎖情景:寫(xiě)鎖操作等待舊的讀鎖的釋放,舊的讀鎖等待新的讀鎖的釋放,新的讀鎖等待寫(xiě)鎖的釋放,形成死鎖。
package main

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

func main() {
        var rw sync.RWMutex
        var count int

        write := func() {
                rw.Lock()
                defer rw.Unlock()
                count++
                fmt.Println("Write:", count)
        }

        read := func() {
                rw.RLock()
                defer rw.RUnlock()
                fmt.Println("Read:", count)
        }

        // 啟動(dòng)多個(gè)讀操作
        for i := 0; i < 5; i++ {
                go read()
        }

        // 啟動(dòng)寫(xiě)操作
        go write()

        time.Sleep(time.Second)
}

在上述代碼中,多個(gè)goroutine同時(shí)調(diào)用read函數(shù),通過(guò)RWMutex來(lái)確保對(duì)共享變量count的讀取是安全的。同時(shí),write函數(shù)用于更新共享變量count,確保在寫(xiě)操作時(shí)獨(dú)占訪問(wèn)權(quán)。

死鎖

什么是死鎖

死鎖指的是一組進(jìn)程由于相互持有和等待資源,導(dǎo)致無(wú)法繼續(xù)執(zhí)行的狀態(tài)。在這種情況下,所有相關(guān)的進(jìn)程都會(huì)無(wú)限期阻塞,無(wú)法向前推進(jìn)。具體來(lái)說(shuō),死鎖發(fā)生在一個(gè)進(jìn)程持有某些資源并等待其他進(jìn)程釋放其占有的資源,同時(shí)這些其他進(jìn)程也在等待第一個(gè)進(jìn)程釋放資源,形成相互等待的狀態(tài)。

死鎖的必要條件

死鎖的發(fā)生需要滿足以下四個(gè)必要條件。

  1. 互斥條件:資源同一時(shí)間只能被一個(gè)進(jìn)程所擁有。
  2. 請(qǐng)求和保持條件:一個(gè)進(jìn)程已經(jīng)擁有某些資源,但在等待其他資源時(shí)不釋放已持有的資源。
  3. 不可剝奪條件:進(jìn)程持有的資源在未使用完畢前,不能被強(qiáng)行剝奪,只能由進(jìn)程自己釋放。
  4. 循環(huán)等待條件:存在一個(gè)進(jìn)程集合中的每個(gè)進(jìn)程都在等待另一個(gè)進(jìn)程所持有的資源,形成一個(gè)循環(huán)等待鏈。

如何解決死鎖問(wèn)題

為了解決死鎖問(wèn)題,可以采取以下兩種策略。

  1. 檢測(cè)和恢復(fù):系統(tǒng)可以定期檢測(cè)死鎖的存在,并采取措施恢復(fù)。例如,通過(guò)回滾進(jìn)程的一部分操作或強(qiáng)制剝奪資源。
  2. 破壞死鎖的必要條件是,可以通過(guò)設(shè)計(jì)系統(tǒng)來(lái)破壞死鎖的四個(gè)必要條件之一,例如:
  • 破壞互斥條件:盡量使用共享資源來(lái)減少互斥性。
  • 破壞請(qǐng)求和保持條件:在進(jìn)程開(kāi)始時(shí)一次性請(qǐng)求所有資源,或者在請(qǐng)求新的資源之前釋放已持有的資源。
  • 破壞不可剝奪條件:設(shè)計(jì)成可以強(qiáng)制剝奪資源,如通過(guò)優(yōu)先級(jí)調(diào)度。
  • 破壞循環(huán)等待條件:對(duì)資源進(jìn)行排序,并要求進(jìn)程按序請(qǐng)求資源,避免形成循環(huán)等待。

示例代碼

以下是一個(gè)Go語(yǔ)言中的死鎖示例,展示了兩個(gè)goroutine由于相互等待對(duì)方持有的資源而導(dǎo)致的死鎖:

package main

import (
        "fmt"
        "sync"
)

func main() {
        var mutexA, mutexB sync.Mutex

        go func() {
                mutexA.Lock()
                fmt.Println("Goroutine 1: Locked mutexA")
                // Simulate some work
                mutexB.Lock()
                fmt.Println("Goroutine 1: Locked mutexB")
                mutexB.Unlock()
                mutexA.Unlock()
        }()

        go func() {
                mutexB.Lock()
                fmt.Println("Goroutine 2: Locked mutexB")
                // Simulate some work
                mutexA.Lock()
                fmt.Println("Goroutine 2: Locked mutexA")
                mutexA.Unlock()
                mutexB.Unlock()
        }()

        // Wait for goroutines to finish (they won't due to deadlock)
        select {}
}

在上述代碼中,兩個(gè)goroutine分別持有mutexAmutexB,并且嘗試獲取對(duì)方的鎖,導(dǎo)致死鎖發(fā)生。每個(gè)goroutine無(wú)限期等待對(duì)方釋放資源,形成相互等待的循環(huán)。

通過(guò)了解死鎖的概念、必要條件及解決策略,我們可以更好地設(shè)計(jì)并發(fā)程序,避免陷入死鎖狀態(tài)。

WaitGroup

基本概念

WaitGroup 是 Go 語(yǔ)言的 sync 包下提供的一種并發(fā)原語(yǔ),用來(lái)解決并發(fā)編排的問(wèn)題。它主要用于等待一組 goroutine 完成。假設(shè)一個(gè)大任務(wù)需要等待三個(gè)小任務(wù)完成才能繼續(xù)執(zhí)行,如果采用輪詢的方法,可能會(huì)導(dǎo)致兩個(gè)問(wèn)題:一是小任務(wù)已經(jīng)完成但大任務(wù)需要很久才能被輪詢到,二是輪詢會(huì)造成 CPU 資源的浪費(fèi)。因此,WaitGroup 通過(guò)阻塞等待并喚醒大任務(wù)的 goroutine 來(lái)解決這個(gè)問(wèn)題。

基本用法

WaitGroup 提供了三個(gè)方法:Add、Done 和 Wait。

  • Add(delta int):將計(jì)數(shù)器增加 delta 值。
  • Done():將計(jì)數(shù)器的值減一,相當(dāng)于 Add(-1)。
  • Wait():阻塞等待,直到計(jì)數(shù)器的值變?yōu)?0,然后喚醒調(diào)用者。

實(shí)現(xiàn)原理

WaitGroup 維護(hù)了兩個(gè)計(jì)數(shù)器,一個(gè)是 v 計(jì)數(shù)器,另一個(gè)是 w 計(jì)數(shù)器。

  • 調(diào)用 Add 方法時(shí),v 計(jì)數(shù)器的值會(huì)增加相應(yīng)的 delta 值。
  • 調(diào)用 Done 方法時(shí),v 計(jì)數(shù)器的值會(huì)減一。
  • 調(diào)用 Wait 方法時(shí),w 計(jì)數(shù)器的值會(huì)加一。當(dāng) v 計(jì)數(shù)器的值為 0 時(shí),會(huì)喚醒所有的 waiter。

易錯(cuò)場(chǎng)景

使用 WaitGroup 需要注意以下易錯(cuò)場(chǎng)景:

  • 計(jì)數(shù)器的值為負(fù)數(shù)會(huì)引發(fā) panic;
  • v 計(jì)數(shù)器增加的值大于減少的值,會(huì)造成一直阻塞。

示例代碼

以下是一個(gè)使用 WaitGroup 的示例代碼:

package main

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

func worker(id int, wg *sync.WaitGroup) {
        defer wg.Done() // Done() 方法用于減少計(jì)數(shù)器
        fmt.Printf("Worker %d starting\n", id)
        time.Sleep(time.Second)
        fmt.Printf("Worker %d done\n", id)
}

func main() {
        var wg sync.WaitGroup

        for i := 1; i <= 3; i++ {
                wg.Add(1) // Add() 方法增加計(jì)數(shù)器
                go worker(i, &wg)
        }

        wg.Wait() // Wait() 方法阻塞等待所有計(jì)數(shù)器為 0
        fmt.Println("All workers done")
}

在上述代碼中,main 函數(shù)創(chuàng)建了一個(gè) WaitGroup 并啟動(dòng)了三個(gè) goroutine,每個(gè) goroutine 執(zhí)行 worker 函數(shù)。在 worker 函數(shù)中,調(diào)用 wg.Done() 方法表示當(dāng)前工作已經(jīng)完成。main 函數(shù)中的 wg.Wait() 方法阻塞等待,直到所有的 goroutine 都完成工作并調(diào)用了 Done 方法。

小結(jié)

WaitGroup 是 Go 語(yǔ)言中非常有用的并發(fā)原語(yǔ),用于等待一組 goroutine 完成。通過(guò)合理使用 Add、Done 和 Wait 方法,可以避免輪詢等待帶來(lái)的性能問(wèn)題,并提高并發(fā)編排的效率。在使用 WaitGroup 時(shí),需要注意計(jì)數(shù)器的增減操作,避免引發(fā) panic 或長(zhǎng)時(shí)間阻塞。

Channel

基本概念

Go 語(yǔ)言提倡通過(guò)通信來(lái)實(shí)現(xiàn)共享內(nèi)存,而不是通過(guò)共享內(nèi)存來(lái)通信。Go 的 CSP(Communicating Sequential Processes)并發(fā)模型正是通過(guò) Goroutine 和 Channel 來(lái)實(shí)現(xiàn)的。Channel 是 Go 語(yǔ)言中用于 goroutine 之間通信的主要工具。

應(yīng)用場(chǎng)景

Channel 有以下幾類應(yīng)用場(chǎng)景。

  1. 數(shù)據(jù)交互:通過(guò) Channel 可以模擬并發(fā)的 Buffer 或者 Queue,實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式。
  2. 數(shù)據(jù)傳遞:通過(guò) Channel 將數(shù)據(jù)傳遞給其他的 goroutine 進(jìn)行處理。
  3. 信號(hào)通知:Channel 可以用于傳遞一些信號(hào),如 close、data ready 等。
  4. 并發(fā)編排:通過(guò) Channel 的阻塞等待機(jī)制,可以讓一組 goroutine 按照一定的順序并發(fā)或串行執(zhí)行。
  5. 實(shí)現(xiàn)鎖功能:通過(guò) Channel 的阻塞等待機(jī)制,可以實(shí)現(xiàn)互斥鎖的功能。

基本用法

Channel 有三種類型:

  1. 只能接收的 Channel:<-chan T。
  2. 只能發(fā)送的 Channel:chan<- T。
  3. 既能發(fā)送又能接收的 Channel:chan T。

Channel 通過(guò) make 函數(shù)進(jìn)行初始化,未初始化的 Channel 的零值是 nil,對(duì) nil 的 Channel 進(jìn)行接收或發(fā)送操作會(huì)導(dǎo)致阻塞。

Channel 可以分為有緩沖和無(wú)緩沖兩種。無(wú)緩沖的 Channel 是同步的,有緩沖的 Channel 是異步的。發(fā)送操作只有在 Channel 滿時(shí)才會(huì)阻塞,接收操作只有在 Channel 為空時(shí)才會(huì)阻塞。

發(fā)送操作是 chan<-,接收操作是 <-chan。接收數(shù)據(jù)時(shí)可以返回兩個(gè)值,第一個(gè)是元素,第二個(gè)是一個(gè)布爾值,若為 false 則說(shuō)明 Channel 已經(jīng)被關(guān)閉并且 Channel 中沒(méi)有緩存的數(shù)據(jù)。

Go 的內(nèi)建函數(shù) close、cap、len 都可以操作 Channel 類型,發(fā)送和接收都可以作為 select 語(yǔ)句的 case,Channel 也可以應(yīng)用于 for range 語(yǔ)句。

實(shí)現(xiàn)原理

發(fā)送

在發(fā)送數(shù)據(jù)給 Channel 時(shí),發(fā)送語(yǔ)句會(huì)轉(zhuǎn)化為 chansend 函數(shù):

  • 如果 Channel 是 nil,調(diào)用者會(huì)被阻塞;
  • 如果 Channel 已經(jīng)關(guān)閉,發(fā)送操作會(huì)導(dǎo)致 panic;
  • 如果 recvq 字段有 receiver,則將數(shù)據(jù)交給它,而不需要放入 buffer 中;
  • 如果沒(méi)有 receiver,則將數(shù)據(jù)放入 buffer 中;
  • 如果 buffer 滿了,則發(fā)送者 goroutine 會(huì)加入到 sendq 中阻塞休眠,直到被喚醒。

接收

在接收數(shù)據(jù)時(shí),接收語(yǔ)句會(huì)轉(zhuǎn)化為 chanrecv 函數(shù):

  • 如果 Channel 是 nil,調(diào)用者會(huì)被阻塞;
  • 如果 Channel 已經(jīng)被關(guān)閉,并且隊(duì)列中無(wú)緩存元素,則返回 false 和一個(gè)對(duì)應(yīng)元素的零值;
  • 如果 sendq 中有 sender 并且 buffer 中有數(shù)據(jù),則優(yōu)先從 buffer 中取出,否則從 sendq 中彈出一個(gè) sender,把它的數(shù)據(jù)復(fù)制給 receiver;
  • 如果沒(méi)有 sender,則從 buffer 中正常取一個(gè)元素;如果沒(méi)有元素,則 receiver 會(huì)加入到 recvq 中阻塞等待,直到接收到數(shù)據(jù)或者 Channel 被關(guān)閉。

關(guān)閉

  • 如果 Channel 是 nil,關(guān)閉 nil 的 Channel 會(huì)導(dǎo)致 panic。
  • 如果關(guān)閉已經(jīng)關(guān)閉的 Channel 也會(huì)導(dǎo)致 panic。
  • 否則將 recvq 和 sendq 全部清除并喚醒。

示例代碼

以下是一個(gè)使用 Channel 的示例代碼:

package main

import (
        "fmt"
        "time"
)

// 生產(chǎn)者:生成數(shù)據(jù)并發(fā)送到 channel
func producer(ch chan<- int, count int) {
        for i := 0; i < count; i++ {
                ch <- i
                fmt.Println("Produced:", i)
                time.Sleep(time.Millisecond * 500)
        }
        close(ch) // 關(guān)閉 channel,表示生產(chǎn)結(jié)束
}

// 消費(fèi)者:從 channel 接收數(shù)據(jù)并處理
func consumer(ch <-chan int) {
        for data := range ch {
                fmt.Println("Consumed:", data)
                time.Sleep(time.Millisecond * 1000)
        }
}

func main() {
        ch := make(chan int, 5) // 創(chuàng)建一個(gè)帶緩沖的 channel
        go producer(ch, 10)     // 啟動(dòng)生產(chǎn)者
        consumer(ch)            // 啟動(dòng)消費(fèi)者
}

在上述代碼中,main 函數(shù)創(chuàng)建了一個(gè)帶緩沖的 Channel,并啟動(dòng)了一個(gè)生產(chǎn)者 goroutine 和一個(gè)消費(fèi)者 goroutine。生產(chǎn)者不斷生成數(shù)據(jù)并發(fā)送到 Channel 中,消費(fèi)者從 Channel 中接收數(shù)據(jù)并進(jìn)行處理。生產(chǎn)者完成后關(guān)閉 Channel,消費(fèi)者則在接收到所有數(shù)據(jù)后結(jié)束。

責(zé)任編輯:武曉燕 來(lái)源: 王中陽(yáng)
相關(guān)推薦

2024-06-19 10:08:34

GoChannel工具

2023-01-30 15:41:10

Channel控制并發(fā)

2023-02-10 09:40:36

Go語(yǔ)言并發(fā)

2021-09-30 09:21:28

Go語(yǔ)言并發(fā)編程

2023-10-20 13:35:19

GoWaitGroup

2022-10-17 08:07:13

Go 語(yǔ)言并發(fā)編程

2023-11-24 11:15:21

協(xié)程編程

2023-11-27 18:07:05

Go并發(fā)編程

2024-07-08 00:01:00

GPM模型調(diào)度器

2022-03-04 10:07:45

Go語(yǔ)言字節(jié)池

2017-11-10 11:27:48

Go并行算法

2022-04-24 15:29:17

微服務(wù)go

2024-02-21 12:14:00

Gochannel?panic?

2025-03-31 00:01:12

2023-12-04 13:48:00

編 程Atomic

2020-12-27 10:15:44

Go語(yǔ)言channel管道

2024-07-01 08:44:42

Go語(yǔ)言協(xié)程

2023-05-29 09:25:38

GolangSelect

2020-01-14 11:17:33

Go并發(fā)Linux

2023-10-18 15:19:56

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)