Go语言并发编程实战


Go语言并发编程实战

Go语言以其优秀的并发支持而闻名,goroutine和channel是Go并发编程的两大核心概念。相比传统的线程模型,goroutine更轻量、更高效。本文将介绍Go并发编程的核心概念和实战模式。

一、Goroutine基础

// 启动goroutine
go func() {
    fmt.Println("Hello from goroutine")
}()

// 等待goroutine完成
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        fmt.Printf("Worker %d working\n", id)
    }(i)
}
wg.Wait()

// 限制并发数
sem := make(chan struct{}, 5)  // 最多5个并发
for _, url := range urls {
    sem <- struct{}{} // 获取信号量
    go func(u string) {
        defer func() { <-sem }() // 释放信号量
        fetch(u)
    }(url)
}

二、Channel通信

// 无缓冲channel - 同步通信
ch := make(chan string)

go func() {
    ch <- "hello"  // 发送(阻塞直到有人接收)
}()

msg := <-ch      // 接收
fmt.Println(msg)  // hello

// 缓冲channel - 异步通信
ch := make(chan int, 100)

// 生产者
go func() {
    for i := 0; i < 1000; i++ {
        ch <- i  // 缓冲区满时阻塞
    }
    close(ch)
}()

// 消费者
for val := range ch {
    process(val)
}

// select多路复用
select {
case msg := <-ch1:
    fmt.Println("ch1:", msg)
case msg := <-ch2:
    fmt.Println("ch2:", msg)
case <-time.After(5 * time.Second):
    fmt.Println("timeout")
}

三、常见并发模式

// Fan-Out/Fan-In - 扇出扇入
func fanOutFanIn(inputs []string) []Result {
    ch := make(chan Result, len(inputs))
    
    // Fan-Out: 多个goroutine处理
    for _, input := range inputs {
        go func(in string) {
            ch <- process(in)
        }(input)
    }
    
    // Fan-In: 收集结果
    var results []Result
    for i := 0; i < len(inputs); i++ {
        results = append(results, <-ch)
    }
    return results
}

// Pipeline - 管道模式
func pipeline(input <-chan Data) <-chan Result {
    stage1 := processStage1(input)
    stage2 := processStage2(stage1)
    return processStage3(stage2)
}

// Context取消
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

select {
case result := <-doWork(ctx):
    fmt.Println(result)
case <-ctx.Done():
    fmt.Println("cancelled:", ctx.Err())
}

四、sync包常用工具

// Mutex - 互斥锁
var mu sync.Mutex
var counter int

func increment() {
    mu.Lock()
    counter++
    mu.Unlock()
}

// sync.Map - 并发安全Map
var m sync.Map
m.Store("key", "value")
val, ok := m.Load("key")
m.Range(func(key, value interface{}) bool {
    fmt.Println(key, value)
    return true  // 继续遍历
})

// sync.Once - 只执行一次
var once sync.Once
once.Do(func() {
    // 初始化操作
})

// sync.Pool - 对象池
var pool = sync.Pool{
    New: func() interface{} {
        return new(Buffer)
    },
}
buf := pool.Get().(*Buffer)
// 使用buf...
pool.Put(buf)

Go的并发模型简洁而强大,goroutine的轻量级特性让并发编程变得容易。但要小心并发安全、goroutine泄漏和死锁等问题。建议在开发中使用race detector(go test -race)来检测数据竞争。


0.049573s