golang實現(xiàn)并發(fā)控制的方法和技巧
前言
golang 是一門支持并發(fā)的編程語言,它提供了 goroutine 和 channel 等強大的特性,讓我們可以輕松地創(chuàng)建和管理多個執(zhí)行單元,實現(xiàn)高效的任務處理。但是,并發(fā)也帶來了一些挑戰(zhàn),比如如何控制 goroutine 的數(shù)量,如何避免資源競爭,如何保證數(shù)據(jù)的一致性等。在本文中,我們將介紹一些 golang 的并發(fā)控制的方法和技巧,希望對你有所幫助。
為什么要控制 goroutine 的數(shù)量
goroutine 是 golang 中最基本的執(zhí)行單元,它是一種輕量級的線程,可以在一個或多個系統(tǒng)線程上運行。goroutine 的創(chuàng)建和調(diào)度都不需要進入內(nèi)核,因此開銷很小,我們可以輕松地創(chuàng)建上百萬個而不會導致系統(tǒng)資源耗盡。那么,我們是不是可以隨心所欲地使用 goroutine,而不用擔心它的數(shù)量呢?
答案是否定的。雖然 goroutine 很輕量,但它也不是免費的,它也會占用一定的內(nèi)存空間,每個 goroutine 至少需要 2KB 的棧空間,如果 goroutine 的數(shù)量過多,就會導致內(nèi)存不足,甚至觸發(fā)頻繁的垃圾回收,影響程序的性能。另外,goroutine 也會消耗 CPU 的時間片,如果 goroutine 的數(shù)量超過 CPU 的核心數(shù),就會導致上下文切換,增加 CPU 的負擔。因此,我們在使用 goroutine 的時候,需要根據(jù)實際的場景和需求,合理地控制 goroutine 的數(shù)量,避免過度并發(fā)。
如何控制 goroutine 的數(shù)量
那么,我們?nèi)绾慰刂?goroutine 的數(shù)量呢?有沒有什么通用的方法或者技巧呢?其實,golang 本身就提供了一些并發(fā)控制的機制,比如 channel 和 sync 包,我們可以利用它們來實現(xiàn) goroutine 的數(shù)量限制。下面,我們就來看一些具體的例子。
利用 channel 的緩沖區(qū)
channel 是 golang 中實現(xiàn)并發(fā)通信的重要工具,它可以在不同的 goroutine 之間傳遞數(shù)據(jù),實現(xiàn)同步和協(xié)作。channel 有兩種類型,一種是無緩沖的 channel,另一種是有緩沖的 channel。無緩沖的 channel 是同步的,發(fā)送和接收操作必須同時發(fā)生,否則會阻塞。有緩沖的 channel 是異步的,它有一個固定大小的緩沖區(qū),可以存儲一定數(shù)量的數(shù)據(jù),發(fā)送操作只有在緩沖區(qū)滿的時候才會阻塞,接收操作只有在緩沖區(qū)空的時候才會阻塞。
我們可以利用有緩沖的 channel 的特性,來實現(xiàn) goroutine 的數(shù)量限制。具體的思路是,我們創(chuàng)建一個有緩沖的 channel,緩沖區(qū)的大小就是我們想要限制的 goroutine 的數(shù)量。然后,我們在啟動一個 goroutine 之前,先向 channel 發(fā)送一個空結(jié)構體,如果 channel 滿了,就會阻塞,直到有其他 goroutine 退出,從 channel 接收一個空結(jié)構體,釋放緩沖區(qū)。這樣,我們就可以保證同時運行的 goroutine 的數(shù)量不會超過 channel 的緩沖區(qū)大小。下面是一個簡單的例子:
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup ch := make(chan struct{}, 3) // 創(chuàng)建一個緩沖區(qū)大小為 3 的 channel for i := 0; i < 10; i++ { ch <- struct{}{} // 向 channel 發(fā)送一個空結(jié)構體,如果 channel 滿了,就會阻塞 wg.Add(1) go func(i int) { defer wg.Done() fmt.Println(i) // 做一些業(yè)務邏輯處理 time.Sleep(time.Second) <-ch // 從 channel 接收一個空結(jié)構體,釋放緩沖區(qū) }(i) } wg.Wait() }
運行結(jié)果如下:
0
1
2
3
4
5
6
7
8
9
從結(jié)果中可以看到,每秒鐘只并發(fā)執(zhí)行了 3 個 goroutine,達到了我們的目的。這種方法的優(yōu)點是簡單易用,缺點是需要手動管理 channel 的發(fā)送和接收,如果忘記了,就會導致 goroutine 泄露或者死鎖。
利用 sync 包
sync 包是 golang 提供的一個并發(fā)同步的包,它提供了一些常用的同步原語,比如互斥鎖,條件變量,等待組等。其中,等待組(WaitGroup)是一個非常有用的工具,它可以用來等待一組 goroutine 的完成。WaitGroup 對象內(nèi)部有一個計數(shù)器,最初從 0 開始,它有三個方法:Add,Done,Wait。Add 方法用來增加計數(shù)器的值,Done 方法用來減少計數(shù)器的值,Wait 方法用來阻塞,直到計數(shù)器的值為 0。
我們可以利用 WaitGroup 來實現(xiàn) goroutine 的數(shù)量限制。具體的思路是,我們創(chuàng)建一個 WaitGroup 對象,然后在啟動一個 goroutine 之前,先調(diào)用 Add 方法,增加計數(shù)器的值,如果計數(shù)器的值達到了我們想要限制的 goroutine 的數(shù)量,就會阻塞,直到有其他 goroutine 結(jié)束,調(diào)用 Done 方法,減少計數(shù)器的值,解除阻塞。這樣,我們就可以保證同時運行的 goroutine 的數(shù)量不會超過我們設定的值。下面是一個簡單的例子:
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup limit := 3 // 限制 goroutine 的數(shù)量為 3 for i := 0; i < 10; i++ { wg.Add(1) // 增加計數(shù)器的值,如果計數(shù)器的值達到 limit,就會阻塞 go func(i int) { defer wg.Done() fmt.Println(i) // 做一些業(yè)務邏輯處理 time.Sleep(time.Second) }(i) if i >= limit { wg.Wait() // 等待其他 goroutine 結(jié)束,減少計數(shù)器的值,解除阻塞 } } wg.Wait() }
運行結(jié)果如下:
0
1
2
3
4
5
6
7
8
9
從結(jié)果中可以看到,每秒鐘只并發(fā)執(zhí)行了 3 個 goroutine,達到了我們的目的。這種方法的優(yōu)點是不需要額外的 channel,缺點是需要手動管理 WaitGroup 的 Add 和 Done 方法,如果忘記了,也會導致 goroutine 泄露或者死鎖。
總結(jié)
在本文中,我們介紹了為什么要控制 goroutine 的數(shù)量,以及如何使用 golang 的 channel 和 sync 包來實現(xiàn) goroutine 的數(shù)量限制。這些方法都是基于 golang 的并發(fā)特性,不需要引入第三方的庫或者框架,可以方便地應用在實際的項目中。當然,這些方法并不是唯一的,也不一定是最優(yōu)的,你可以根據(jù)你的具體的場景和需求,選擇合適的方法,或者自己設計更好的方法,來實現(xiàn) goroutine 的數(shù)量限制。
以上就是golang實現(xiàn)并發(fā)控制的方法和技巧的詳細內(nèi)容,更多關于golang并發(fā)控制的資料請關注腳本之家其它相關文章!
相關文章
Golang并發(fā)繞不開的重要組件之Goroutine詳解
Goroutine、Channel、Context、Sync都是Golang并發(fā)編程中的幾個重要組件,這篇文中主要為大家介紹了Goroutine的相關知識,需要的可以參考一下2023-06-06