Go 語言中的併發模式
Go 語言以其併發性和輕量級的 goroutine 而聞名,學習如何使用和處理它們是最具挑戰性的任務。在本文中,我將展示一些併發模式及其使用場景,以幫助您識別所需場景的模式。
- Goroutine
package main
import (
"fmt"
"time"
)
func main() {
go sayHello() // 啓動goroutine
time.Sleep(1 * time.Second) // 等待goroutine完成
}
func sayHello() {
fmt.Println("Hello, World!")
}
使用場景
當您需要執行非阻塞操作時,例如在 Web 服務器中處理請求或執行後臺任務。
優點
-
輕量級且易於啓動。
-
提供簡單的併發性,無需複雜的結構。
缺點
-
如果共享數據處理不當,可能導致競爭條件。
-
由於執行流程非線性,調試可能更加複雜。
- 通道(Channel)
package main
import (
"fmt"
)
func main() {
ch := make(chan string) // 創建通道
go func() {
ch <- "Hello from Goroutine!" // 發送數據到通道
}()
message := <-ch // 從通道接收數據
fmt.Println(message)
}
使用場景
在 goroutine 之間通信或同步執行。
優點
-
在 goroutine 之間安全通信。
-
簡單的同步機制。
缺點
-
如果過度使用或誤用,可能引入複雜性。
-
如果通道處理不當,可能發生死鎖。
- 工作池(Worker Pool)
package main
import (
"fmt"
"sync"
)
type Job struct {
ID int
}
func worker(id int, jobs <-chan Job, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, job.ID)
}
}
func main() {
const numWorkers = 3
jobs := make(chan Job, 10) // 緩衝通道
var wg sync.WaitGroup
for w := 1; w <= numWorkers; w++ {
wg.Add(1)
go worker(w, jobs, &wg)
}
for j := 1; j <= 5; j++ {
jobs <- Job{ID: j} // 發送任務到通道
}
close(jobs) // 關閉任務通道
wg.Wait() // 等待所有工作完成
}
使用場景
當您有許多任務可以併發處理,但希望限制併發操作的數量時。
優點
-
通過控制併發 goroutine 的數量來限制資源使用。
-
易於高效地處理大量任務。
缺點
-
比簡單的 goroutine 實現更復雜。
-
需要仔細管理任務分配和完成。
- 扇出扇入(Fan-Out, Fan-In)
package main
import (
"fmt"
"sync"
)
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
results <- job * 2 // 處理任務
}
}
func main() {
jobs := make(chan int, 10)
results := make(chan int, 10)
var wg sync.WaitGroup
for w := 1; w <= 3; w++ { // 3個工作者
wg.Add(1)
go worker(w, jobs, results, &wg)
}
go func() {
wg.Wait()
close(results) // 完成後關閉結果通道
}()
for j := 1; j <= 5; j++ {
jobs <- j // 發送任務
}
close(jobs) // 關閉任務通道
for result := range results {
fmt.Println("Result:", result) // 收集結果
}
}
使用場景
當您需要將工作分配給多個 goroutine 並整合結果時。
優點
-
高效地平衡多個工作者之間的工作負載。
-
簡化結果收集。
缺點
-
由於多個通道,稍微複雜一些。
-
需要仔細管理同步。
- 用於取消的上下文(Context for Cancellation)
package main
import (
"context"
"fmt"
"time"
)
func doWork(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Work canceled")
return
default:
fmt.Println("Working...")
time.Sleep(500 * time.Millisecond) // 模擬工作
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go doWork(ctx)
time.Sleep(2 * time.Second) // 讓它工作一段時間
cancel() // 取消工作
time.Sleep(1 * time.Second) // 等待取消
}
使用場景
當您需要管理 goroutine 的取消或超時時。
優點
-
提供一種結構化的方式來取消 goroutine。
-
可以使用單個上下文管理多個 goroutine。
缺點
-
如果管理不當,可能會使邏輯複雜化。
-
需要理解上下文傳播。
- 管道(Pipeline)
package main
import (
"fmt"
)
func square(input <-chan int, output chan<- int) {
for num := range input {
output <- num * num // 平方數
}
close(output) // 完成後關閉輸出
}
func main() {
input := make(chan int)
output := make(chan int)
go square(input, output)
for i := 1; i <= 5; i++ {
input <- i // 發送數字
}
close(input) // 關閉輸入通道
for result := range output {
fmt.Println("Squared:", result) // 打印結果
}
}
使用場景
當您希望分階段處理數據時。
優點
-
促進模塊化和關注點分離。
-
更容易管理和理解數據流。
缺點
-
隨着階段的增多,可能變得複雜。
-
需要仔細管理通道。
- 速率限制(Rate Limiting)
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(1 * time.Second) // 設置速率限制
defer ticker.Stop()
go func() {
for range ticker.C {
fmt.Println("Tick: Doing work...")
// 在這裏執行工作
}
}()
// 模擬工作5秒
time.Sleep(5 * time.Second)
}
使用場景
當您需要限制操作速率時,例如 API 調用。
優點
-
控制操作頻率的簡單有效方法。
-
減少資源爭用。
缺點
-
如果限制過於嚴格,可能會延遲處理。
-
需要仔細配置速率限制。
- Select 語句
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch1 <- "Result from channel 1"
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- "Result from channel 2"
}()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
case <-time.After(3 * time.Second):
fmt.Println("Timeout!")
}
}
使用場景
當您需要等待多個通道並根據哪個通道首先接收到數據來執行操作時。
優點
-
提供處理多個異步操作的方法。
-
可以有效地實現超時。
缺點
-
如果處理不當,可能導致複雜的邏輯。
-
需要仔細處理所有情況以避免信號丟失。
總結
這些模式是編寫 Go 語言併發程序的基礎。每種模式都有其使用場景、優點和缺點,您可能會發現結合多種模式可以爲您的應用程序提供最佳解決方案。仔細考慮應用程序的具體需求,並選擇合適的模式。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/nCzu3LyBbe4EcWAYIIGq5A