Tech Blog

Exploring Technology, Innovation & Future

繁體中文ProgrammingSeptember 1, 2025

Go並發程式設計:Goroutines與Channels深度解析

Go語言最引人注目的特性之一就是其內建的並發支援。通過goroutines和channels,Go提供了一種優雅且高效的方式來處理並發任務。本文將深入探討Go的並發模式和最佳實踐。

Goroutines:輕量級執行緒

什麼是Goroutines? Goroutines是Go語言中的輕量級執行緒,由Go運行時管理。它們比作業系統執行緒更加輕量,一個Go程式可以輕鬆運行數萬個goroutines。

基本用法: ```go func sayHello(name string) { fmt.Printf("Hello, %s!\n", name) }

func main() { go sayHello("Alice") // 在新的goroutine中執行 go sayHello("Bob") time.Sleep(time.Second) // 等待goroutines完成 } ```

Goroutines的特點 - **輕量級**:初始堆疊只有2KB - **動態調整**:堆疊可以根據需要自動增長和縮小 - **M:N調度**:多個goroutines映射到少數的OS執行緒上

Channels:Goroutines之間的通信

Channel基礎 Channels是goroutines之間通信的管道,遵循"不要通過共享記憶體來通信,而要通過通信來共享記憶體"的設計哲學。

無緩衝channel: ```go func main() { ch := make(chan string) go func() { ch <- "Hello from goroutine!" }() message := <-ch fmt.Println(message) } ```

有緩衝channel: ```go func main() { ch := make(chan int, 3) // 容量為3的緩衝channel ch <- 1 ch <- 2 ch <- 3 fmt.Println(<-ch) // 1 fmt.Println(<-ch) // 2 fmt.Println(<-ch) // 3 } ```

常見並發模式

1. Worker Pool模式 ```go func workerPool() { const numWorkers = 3 jobs := make(chan int, 100) results := make(chan int, 100) // 啟動workers for w := 1; w <= numWorkers; w++ { go worker(w, jobs, results) } // 發送工作 for j := 1; j <= 9; j++ { jobs <- j } close(jobs) // 收集結果 for a := 1; a <= 9; a++ { <-results } }

func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("Worker %d started job %d\n", id, j) time.Sleep(time.Second) fmt.Printf("Worker %d finished job %d\n", id, j) results <- j * 2 } } ```

2. Pipeline模式 ```go func pipeline() { numbers := make(chan int) squares := make(chan int) // 階段1:生成數字 go func() { for i := 1; i <= 5; i++ { numbers <- i } close(numbers) }() // 階段2:計算平方 go func() { for n := range numbers { squares <- n * n } close(squares) }() // 階段3:輸出結果 for s := range squares { fmt.Println(s) } } ```

3. Fan-out/Fan-in模式 ```go func fanOutFanIn() { input := make(chan int) // Fan-out: 將工作分散到多個goroutines c1 := worker2(input) c2 := worker2(input) // Fan-in: 合併多個channels的結果 output := fanIn(c1, c2) // 發送工作 go func() { for i := 1; i <= 10; i++ { input <- i } close(input) }() // 接收結果 for result := range output { fmt.Println(result) } }

func worker2(input <-chan int) <-chan int { output := make(chan int) go func() { for n := range input { output <- n * n } close(output) }() return output }

func fanIn(inputs ...<-chan int) <-chan int { output := make(chan int) var wg sync.WaitGroup for _, input := range inputs { wg.Add(1) go func(ch <-chan int) { defer wg.Done() for n := range ch { output <- n } }(input) } go func() { wg.Wait() close(output) }() return output } ```

Select語句:多路復用

Select語句讓goroutine可以等待多個channel操作:

```go func selectExample() { c1 := make(chan string) c2 := make(chan string) go func() { time.Sleep(1 * time.Second) c1 <- "來自c1" }() go func() { time.Sleep(2 * time.Second) c2 <- "來自c2" }() for i := 0; i < 2; i++ { select { case msg1 := <-c1: fmt.Println("收到", msg1) case msg2 := <-c2: fmt.Println("收到", msg2) } } } ```

並發安全和同步

Mutex使用 ```go type SafeCounter struct { mu sync.Mutex value int }

func (c *SafeCounter) Inc() { c.mu.Lock() defer c.mu.Unlock() c.value++ }

func (c *SafeCounter) Value() int { c.mu.Lock() defer c.mu.Unlock() return c.value } ```

最佳實踐

1. 避免Goroutine洩漏:確保所有goroutines都能正常結束 2. 適當使用緩衝:根據實際需求選擇channel緩衝大小 3. 錯誤處理:在並發環境中妥善處理錯誤 4. 使用Context:用於控制goroutines的生命週期 5. 性能測試:使用Go的基準測試工具測試並發性能

Go的並發模型為開發高性能、可擴展的應用程式提供了強大的基礎。通過掌握這些模式和最佳實踐,你可以充分發揮Go語言在並發程式設計方面的優勢。

Related Articles