Go語言中的併發模式

技术颜良發表於2024-11-03

Go語言中的併發模式

原創 Go 源自開發者
源自開發者
專注於提供關於Go語言的實用教程、案例分析、最新趨勢,以及雲原生技術的深度解析和實踐經驗分享。
373篇原創內容

Go語言以其併發性和輕量級的goroutine而聞名,學習如何使用和處理它們是最具挑戰性的任務。在本文中,我將展示一些併發模式及其使用場景,以幫助您識別所需場景的模式。

1. Goroutine

package main

import (
"fmt"
"time"
)

func main() {
go sayHello() // 啟動goroutine
time.Sleep(1 * time.Second) // 等待goroutine完成
}

func sayHello() {
fmt.Println("Hello, World!")
}

使用場景

當您需要執行非阻塞操作時,例如在Web伺服器中處理請求或執行後臺任務。

優點

  • 輕量級且易於啟動。
  • 提供簡單的併發性,無需複雜的結構。

缺點

  • 如果共享資料處理不當,可能導致競爭條件。
  • 由於執行流程非線性,除錯可能更加複雜。

2. 通道(Channel)

package main

import (
"fmt"
)

func main() {
ch := make(chan string) // 建立通道

go func() {
ch <- "Hello from Goroutine!" // 傳送資料到通道
}()

message := <-ch // 從通道接收資料
fmt.Println(message)
}

使用場景

在goroutine之間通訊或同步執行。

優點

  • 在goroutine之間安全通訊。
  • 簡單的同步機制。

缺點

  • 如果過度使用或誤用,可能引入複雜性。
  • 如果通道處理不當,可能發生死鎖。

3. 工作池(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實現更復雜。
  • 需要仔細管理任務分配和完成。

4. 扇出扇入(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並整合結果時。

優點

  • 高效地平衡多個工作者之間的工作負載。
  • 簡化結果收集。

缺點

  • 由於多個通道,稍微複雜一些。
  • 需要仔細管理同步。

5. 用於取消的上下文(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。

缺點

  • 如果管理不當,可能會使邏輯複雜化。
  • 需要理解上下文傳播。

6. 管道(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) // 列印結果
}
}

使用場景

當您希望分階段處理資料時。

優點

  • 促進模組化和關注點分離。
  • 更容易管理和理解資料流。

缺點

  • 隨著階段的增多,可能變得複雜。
  • 需要仔細管理通道。

7. 速率限制(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呼叫。

優點

  • 控制操作頻率的簡單有效方法。
  • 減少資源爭用。

缺點

  • 如果限制過於嚴格,可能會延遲處理。
  • 需要仔細配置速率限制。

8. 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語言併發程式的基礎。每種模式都有其使用場景、優點和缺點,您可能會發現結合多種模式可以為您的應用程式提供最佳解決方案。仔細考慮應用程式的具體需求,並選擇合適的模式。

圖片


文章精選

使用 Go 語言連線並操作 SQLite 資料庫

Go語言官方團隊推薦的依賴注入工具

替代zap,Go語言官方實現的結構化日誌包

Go語言常見錯誤 | 不使用function option模式

必看| Go語言專案結構最佳實踐

源自下載
點選新增進交流群,免費領取Go語言學習資料!

Golang · 目錄
上一篇在 Docker 中執行 Golang 應用程式
閱讀原文
閱讀 1278

相關文章