Go并发编程模式与实战技巧:从Goroutine到Channel的深度实践
2026/5/14 16:40:14 网站建设 项目流程

Go并发编程模式与实战技巧:从Goroutine到Channel的深度实践

大家好,我是迪哥。Go 的并发模型是它最迷人的特性之一,从 Goroutine 到 Channel,从 Select 到 Context,掌握这些模式能让你的代码飞起来。今天就聊聊 Go 并发编程的最佳实践。

并发基础

Goroutine

// 启动一个 Goroutine go func() { fmt.Println("Hello from goroutine") }() // 带参数的 Goroutine go sayHello("World") func sayHello(name string) { fmt.Printf("Hello, %s!\n", name) }

Channel

// 有缓冲 Channel ch := make(chan int, 100) // 发送数据 ch <- 42 // 接收数据 val := <-ch // 关闭 Channel close(ch)

Select

select { case <-ch1: fmt.Println("Received from ch1") case <-ch2: fmt.Println("Received from ch2") case <-time.After(1 * time.Second): fmt.Println("Timeout") }

并发模式

模式一:Worker Pool

func worker(id int, jobs <-chan int, results chan<- int) { for job := range jobs { fmt.Printf("Worker %d processing job %d\n", id, job) results <- job * 2 } } func main() { jobs := make(chan int, 100) results := make(chan int, 100) // 启动 3 个 worker for w := 1; w <= 3; w++ { go worker(w, jobs, results) } // 发送 9 个任务 for j := 1; j <= 9; j++ { jobs <- j } close(jobs) // 收集结果 for r := 1; r <= 9; r++ { <-results } }

模式二:Fan-Out / Fan-In

// Fan-Out:多个 Goroutine 消费同一个 Channel func produce(ch chan<- int) { for i := 0; i < 10; i++ { ch <- i } close(ch) } func consume(id int, in <-chan int, out chan<- int) { for num := range in { out <- num * 2 } } // Fan-In:多个 Channel 的结果汇总到一个 Channel func merge(cs ...<-chan int) <-chan int { var wg sync.WaitGroup out := make(chan int) wg.Add(len(cs)) for _, c := range cs { go func(ch <-chan int) { for n := range ch { out <- n } wg.Done() }(c) } go func() { wg.Wait() close(out) }() return out }

模式三:Pipeline

func gen(nums ...int) <-chan int { out := make(chan int) go func() { for _, n := range nums { out <- n } close(out) }() return out } func sq(in <-chan int) <-chan int { out := make(chan int) go func() { for n := range in { out <- n * n } close(out) }() return out } func main() { // 生成 → 平方 → 打印 for n := range sq(gen(1, 2, 3, 4)) { fmt.Println(n) } }

模式四:Context 取消

func doWork(ctx context.Context) error { for { select { case <-ctx.Done(): return ctx.Err() default: // 正常工作 time.Sleep(100 * time.Millisecond) } } } func main() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() go doWork(ctx) }

并发安全

Mutex

type Counter struct { mu sync.Mutex value int } func (c *Counter) Increment() { c.mu.Lock() defer c.mu.Unlock() c.value++ } func (c *Counter) Value() int { c.mu.Lock() defer c.mu.Unlock() return c.value }

Sync.Map

var cache sync.Map func getFromCache(key string) (interface{}, bool) { return cache.Load(key) } func setToCache(key string, value interface{}) { cache.Store(key, value) }

性能优化

Sync.Pool

var bufferPool = sync.Pool{ New: func() interface{} { return make([]byte, 4096) }, } func processData(data []byte) { buf := bufferPool.Get().([]byte) defer bufferPool.Put(buf) copy(buf, data) // 处理... }

避免 Goroutine 泄漏

// ❌ 泄漏:没有停止机制 go func() { for { doSomething() } }() // ✅ 正确:使用 Channel 控制 stop := make(chan struct{}) go func() { for { select { case <-stop: return default: doSomething() } } }() close(stop) // 停止 Goroutine

常见坑

原因解决方案
Goroutine 泄漏没有退出机制使用 Context 或 Channel 控制
死锁Channel 发送/接收不匹配确保发送和接收配对
数据竞争共享数据未加锁使用 Mutex 或 Channel
内存泄漏Sync.Pool 误用正确使用 Pool,避免存储指针
阻塞主协程忘记启动 Goroutine检查 go 关键字

说到并发,我家那只叫 Docker 的哈士奇最近学会了"并发抢食"——两只碗同时吃,效率翻倍,这并发能力比某些程序员还强 😂

我是迪哥,我们下期再见!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询