Golang協(xié)程中channel和sync的角色扮演分析
Channel
channel 是Go語言中用于協(xié)程(goroutine)之間通信的一種機(jī)制。通過channel,你可以在不同的goroutine之間安全地傳遞數(shù)據(jù)。
代碼示例
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
fmt.Println("主進(jìn)程開始")
done := make(chan bool, 5)
for i := 0; i < 5; i++ {
fmt.Println("等待進(jìn)協(xié)程:" + strconv.Itoa(i))
go worker(i, done)
}
for i := 0; i < 5; i++ {
fmt.Println("等待出協(xié)程:" + strconv.Itoa(i))
<-done
fmt.Println("已經(jīng)出協(xié)程:" + strconv.Itoa(i))
}
close(done)
fmt.Println("主進(jìn)程結(jié)束")
}
func worker(i int, done chan bool) {
time.Sleep(2 * time.Second)
fmt.Println("協(xié)程:" + strconv.Itoa(i))
done <- true
}
Golang緩沖channel和非緩沖channel的區(qū)別說明
Sync
sync 包提供了基本的同步原語,用于在多個(gè)goroutine之間協(xié)調(diào)對共享資源的訪問。
代碼示例
package main
import (
"fmt"
"strconv"
"sync"
"time"
)
func main() {
fmt.Println("主進(jìn)程開始")
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
fmt.Println("等待進(jìn)協(xié)程:" + strconv.Itoa(i))
wg.Add(1)
go worker(i, &wg)
}
fmt.Println("主進(jìn)程等待")
wg.Wait()
fmt.Println("主進(jìn)程結(jié)束")
}
func worker(i int, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(2 * time.Second)
fmt.Println("協(xié)程:" + strconv.Itoa(i))
}
實(shí)際應(yīng)用
問題代碼
package main
import (
"fmt"
"sync"
)
// 定義結(jié)構(gòu)體
type MyStruct struct {
Field1 string
Field2 int
}
func main() {
var myStructs []MyStruct
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
fmt.Println(i)
go worker(&myStructs, &wg, i)
}
wg.Wait()
fmt.Println(myStructs)
fmt.Println(len(myStructs))
}
func worker(data *[]MyStruct, wg *sync.WaitGroup, i int) {
defer wg.Done()
ss := MyStruct{
Field1: fmt.Sprintf("Data %d", i),
Field2: i * 10,
}
*data = append(*data, ss)
}
問題分析
多個(gè) worker 協(xié)程并發(fā)地向共享切片 myStructs 添加元素時(shí)存在競態(tài)風(fēng)險(xiǎn)。由于 Go 語言的切片并非并發(fā)安全,當(dāng)多個(gè)協(xié)程同時(shí)調(diào)用 append 函數(shù)修改切片時(shí),可能導(dǎo)致數(shù)據(jù)競爭或意外行為。
解決方案
Mutex(互斥鎖)解決案例
Golang語言中的互斥鎖和讀寫鎖(Mutex和RWMutex)
package main
import (
"fmt"
"sync"
)
// 定義結(jié)構(gòu)體
type MyStruct struct {
Field1 string
Field2 int
}
func main() {
var myStructs []MyStruct
var mu sync.Mutex
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(&myStructs, &wg, &mu, i)
}
wg.Wait()
fmt.Println(myStructs)
fmt.Println(len(myStructs))
}
func worker(data *[]MyStruct, wg *sync.WaitGroup, mu *sync.Mutex, i int) {
defer wg.Done()
ss := MyStruct{
Field1: fmt.Sprintf("Data %d", i),
Field2: i * 10,
}
mu.Lock()
*data = append(*data, ss)
mu.Unlock()
}
問題雖已解決,但Mutex在高并發(fā)場景下會(huì)導(dǎo)致大量鎖等待,對系統(tǒng)性能造成顯著影響。
Channel 解決案例
package main
import (
"fmt"
"sync"
)
type MyStruct struct {
Field1 string
Field2 int
}
func main() {
var wg sync.WaitGroup
dataChan := make(chan MyStruct, 10)
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(dataChan, &wg, i)
}
go func() {
wg.Wait()
close(dataChan)
}()
var myStructs []MyStruct
for ss := range dataChan {
myStructs = append(myStructs, ss)
}
fmt.Println(myStructs)
fmt.Println(len(myStructs))
}
func worker(dataChan chan<- MyStruct, wg *sync.WaitGroup, i int) {
defer wg.Done()
ss := MyStruct{
Field1: fmt.Sprintf("Data %d", i),
Field2: i * 10,
}
dataChan <- ss
}
結(jié)論
Channel 的角色
channel是Go語言中協(xié)程(goroutine)之間通信的橋梁,它有以下重要作用:
- 協(xié)程間通信:channel提供了一種在goroutine之間安全地傳遞數(shù)據(jù)的方式。通過使用channel,goroutine可以相互發(fā)送和接收數(shù)據(jù),從而實(shí)現(xiàn)協(xié)作和協(xié)調(diào)。
- 同步與協(xié)調(diào):channel的阻塞特性使得goroutine之間的同步變得簡單而直觀。當(dāng)一個(gè)goroutine試圖從一個(gè)沒有數(shù)據(jù)的channel中接收數(shù)據(jù)時(shí),它會(huì)阻塞直到有數(shù)據(jù)可用。同樣,當(dāng)一個(gè)goroutine試圖向一個(gè)已經(jīng)滿的channel發(fā)送數(shù)據(jù)時(shí),它也會(huì)被阻塞。這種阻塞機(jī)制使得goroutine能夠按照預(yù)期的順序進(jìn)行通信和協(xié)作。
- 避免共享狀態(tài):通過使用channel進(jìn)行通信,可以減少對共享狀態(tài)的依賴,從而降低并發(fā)編程中的復(fù)雜性和錯(cuò)誤率。每個(gè)goroutine可以擁有自己的局部變量和狀態(tài),并通過channel與其他goroutine進(jìn)行交互,而無需直接訪問共享內(nèi)存。
Sync 的角色
sync包提供了一組同步原語,用于保護(hù)共享資源和協(xié)調(diào)goroutine的執(zhí)行,確保并發(fā)程序的正確性和一致性。sync包中的關(guān)鍵組件包括:
- 互斥鎖(Mutex):sync.Mutex是一個(gè)互斥鎖,用于保護(hù)共享資源,確保同一時(shí)間只有一個(gè)goroutine可以訪問該資源。通過加鎖和解鎖操作,可以防止多個(gè)goroutine同時(shí)修改共享資源,從而避免競態(tài)條件和數(shù)據(jù)不一致。
- 讀寫鎖(RWMutex):sync.RWMutex是互斥鎖的一種擴(kuò)展,允許多個(gè)goroutine同時(shí)讀取共享資源,但在寫入時(shí)阻塞所有其他goroutine(無論是讀還是寫)。這種讀寫分離的機(jī)制可以提高并發(fā)程序的性能,特別是在讀多寫少的場景中。
- 等待組(WaitGroup):sync.WaitGroup提供了一種等待一組goroutine完成的方法。通過調(diào)用Add方法增加等待組的計(jì)數(shù),并在每個(gè)goroutine結(jié)束時(shí)調(diào)用Done方法減少計(jì)數(shù),主goroutine可以調(diào)用Wait方法阻塞,直到所有g(shù)oroutine都完成執(zhí)行。這有助于協(xié)調(diào)goroutine的執(zhí)行順序和依賴關(guān)系。
- 條件變量(Cond):sync.Cond是一個(gè)條件變量,允許一個(gè)或多個(gè)goroutine等待某個(gè)條件為真,并在該條件變?yōu)檎鏁r(shí)通知它們。條件變量提供了一種更靈活的同步機(jī)制,適用于需要根據(jù)特定條件進(jìn)行等待和通知的場景。
綜上所述,channel和sync包在Go語言的并發(fā)編程中各自扮演著重要的角色。channel用于goroutine之間的通信和同步,而sync包提供了保護(hù)共享資源和協(xié)調(diào)goroutine執(zhí)行的同步原語。它們共同確保了并發(fā)程序的正確性和高效性。
到此這篇關(guān)于Golang協(xié)程中channel和sync的角色扮演分析的文章就介紹到這了,更多相關(guān)go協(xié)程channel和sync內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用golang生成prometheus格式數(shù)據(jù)
Prometheus是一個(gè)開源的監(jiān)控系統(tǒng),擁有許多Advanced?Feature,本文將介紹Primetheus?client的使用,并基于golang生成prometheus格式數(shù)據(jù),希望對大家有所幫助2025-02-02
Golang中map的三種聲明定義方式實(shí)現(xiàn)
本文主要介紹了Golang中map的三種聲明定義方式實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
利用go語言實(shí)現(xiàn)查找二叉樹中的最大寬度
這篇文章主要介紹了利用go語言實(shí)現(xiàn)查找二叉樹中的最大寬度,文章圍繞主題展開詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05

