Go 語言中的併發模式

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 服務器中處理請求或執行後臺任務。

優點

缺點

  1. 通道(Channel)

package main

import (
    "fmt"
)

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

    go func() {
        ch <- "Hello from Goroutine!" // 發送數據到通道
    }()

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

使用場景

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

優點

缺點

  1. 工作池(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() // 等待所有工作完成
}

使用場景

當您有許多任務可以併發處理,但希望限制併發操作的數量時。

優點

缺點

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

優點

缺點

  1. 用於取消的上下文(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 的取消或超時時。

優點

缺點

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

使用場景

當您希望分階段處理數據時。

優點

缺點

  1. 速率限制(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 調用。

優點

缺點

  1. 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