登录
首页 >  Golang >  Go教程

Golang协程同步技巧全解析

时间:2026-04-20 23:42:43 190浏览 收藏

本文深入解析了Golang中协程(goroutine)同步的四大核心机制——sync.WaitGroup、sync.Mutex、channel和sync.Cond,分别从原理、典型用法、适用场景及常见陷阱出发,清晰阐明了如何在不同并发需求下选择最合适的同步方式:WaitGroup用于批量任务等待,Mutex保障共享资源访问安全,channel以“通信代替共享内存”实现优雅解耦,Cond则应对复杂的条件等待逻辑;同时特别提醒开发者警惕死锁、漏解锁、channel误用等高频问题,是Go并发编程实践中不可或缺的同步指南。

如何在Golang中实现协程同步

在Golang中,协程(goroutine)之间的同步主要通过多种机制来保证数据安全和执行顺序。最常用的方式包括使用 sync.Mutexsync.WaitGroupchannel 以及 sync.Cond 等。下面介绍几种典型的同步方法及其适用场景。

使用 sync.WaitGroup 等待协程完成

当你需要等待一组协程全部执行完毕时,sync.WaitGroup 是最常用的工具。

它通过计数器控制主线程阻塞,直到所有协程完成任务并调用 Done()。

  • 调用 Add(n) 设置需要等待的协程数量
  • 每个协程执行完后调用 Done() 减少计数
  • 主线程调用 Wait() 阻塞,直到计数归零

示例:

package main
<p>import (
"fmt"
"sync"
"time"
)</p><p>func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}</p><p>func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("All workers finished")
}
</p>

使用互斥锁 sync.Mutex 保护共享资源

当多个协程访问和修改同一变量时,可能引发竞态条件(race condition)。使用 sync.Mutex 可以确保同一时间只有一个协程能访问临界区。

  • 在访问共享数据前调用 Lock()
  • 操作完成后立即调用 Unlock()
  • 建议配合 defer 使用,防止忘记解锁

示例:并发安全的计数器

package main
<p>import (
"fmt"
"sync"
)</p><p>type Counter struct {
mu   sync.Mutex
val  int
}</p><p>func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.val++
}</p><p>func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.val
}</p><p>func main() {
var wg sync.WaitGroup
counter := &Counter{}</p><pre class="brush:php;toolbar:false"><code>for i := 0; i &lt; 1000; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        counter.Inc()
    }()
}
wg.Wait()
fmt.Println("Counter value:", counter.Value()) // 输出 1000</code>

}

使用 channel 实现协程通信与同步

Go提倡“通过通信共享内存”,而不是“通过共享内存通信”。channel 不仅用于传递数据,还能自然实现协程同步。

  • 无缓冲 channel 的发送和接收是同步的
  • 可用来通知完成、传递结果或控制执行顺序
  • close(channel) 可广播关闭信号

示例:用 channel 等待任务完成

package main
<p>import (
"fmt"
"time"
)</p><p>func doTask(done chan bool) {
fmt.Println("Task starting...")
time.Sleep(2 * time.Second)
fmt.Println("Task done")
done <- true
}</p><p>func main() {
done := make(chan bool)
go doTask(done)
<-done // 阻塞等待
fmt.Println("Main exit")
}
</p>

使用 sync.Cond 实现条件等待

当协程需要等待某个条件成立时,sync.Cond 提供了更细粒度的控制。它结合 mutex 和 signal/broadcast 机制,适合实现生产者-消费者等模式。

常见方法:Wait()、Signal()、Broadcast()

示例:简单信号通知

package main
<p>import (
"fmt"
"sync"
"time"
)</p><p>func main() {
var mu sync.Mutex
cond := sync.NewCond(&mu)</p><pre class="brush:php;toolbar:false"><code>go func() {
    time.Sleep(2 * time.Second)
    mu.Lock()
    fmt.Println("Sending signal")
    cond.Signal()
    mu.Unlock()
}()

mu.Lock()
fmt.Println("Waiting for signal...")
cond.Wait()
mu.Unlock()
fmt.Println("Signal received")</code>

}

基本上就这些。选择哪种方式取决于具体需求:WaitGroup 适合等待批量任务,Mutex 保护共享状态,channel 更适合协程间解耦通信,Cond 用于复杂条件同步。合理使用这些工具,就能写出高效又安全的并发程序。不复杂但容易忽略细节,比如死锁、忘记 unlock 或误用 channel。

终于介绍完啦!小伙伴们,这篇关于《Golang协程同步技巧全解析》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布Golang相关知识,快来关注吧!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>