Golang中goroutine同步问题求助
Golang中goroutine同步问题求助 大家好,
我目前正在开发一个Go项目,goroutine的同步问题让我很困扰。由于我有多个goroutine在并发运行,我需要确保它们都能正确同步。
在多个goroutine共享同一资源的情况下,我希望确保同一时间只有一个goroutine可以访问该共享资源。虽然我听说过Go中的互斥锁(mutex)和通道(channel),但我不确定哪种策略最适合我的情况。
有人能提供关于在Go中正确同步goroutine的指导或示例吗?任何指导或帮助都将不胜感激。
提前感谢!
这个 Go by Example: Mutexes 链接对你有帮助吗?
更多关于Golang中goroutine同步问题求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
package main
import (
"fmt"
"sync"
"time"
)
var (
resource int // 共享资源
mutex sync.Mutex // 用于同步的互斥锁对象
wg sync.WaitGroup // 用于goroutine同步的等待组
)
func main() {
numWorkers := 5 // goroutine的数量
wg.Add(numWorkers)
for i := 0; i < numWorkers; i++ {
go worker(i)
}
wg.Wait() // 等待所有goroutine完成
}
func worker(id int) {
defer wg.Done()
for i := 0; i < 5; i++ {
// 开始对共享资源进行同步
mutex.Lock()
// 访问共享资源并执行操作
resource++
fmt.Printf("Worker %d: Resource value: %d\n", id, resource)
// 结束同步
mutex.Unlock()
time.Sleep(time.Millisecond * 100) // 操作之间的延迟
}
}
在给定的示例中,resource 变量表示由多个goroutine访问的共享资源。mutex 对象是一个用于同步的互斥锁,用于确保对共享资源的独占访问。wg 变量是一个 sync.WaitGroup,用于等待所有goroutine完成。
worker() 函数是每个goroutine执行的函数。它执行5次工作迭代。在执行工作之前,使用 mutex.Lock() 函数锁定互斥锁,防止其他goroutine访问资源。工作完成后,使用 mutex.Unlock() 函数解锁互斥锁,允许其他goroutine访问资源。
通过使用互斥锁来同步对共享资源的访问,可以避免goroutine之间的竞态条件,并保持数据的一致性。
在Go中,goroutine的同步通常可以通过互斥锁(sync.Mutex)或通道(channel)来实现。选择哪种方式取决于具体场景:互斥锁适合保护共享资源的临界区,而通道更适合goroutine间的通信和协调。以下分别提供两种方式的示例代码。
使用互斥锁(sync.Mutex)同步
当多个goroutine需要访问同一共享资源(如变量、数据结构)时,可以使用互斥锁确保同一时间只有一个goroutine进入临界区。
package main
import (
"fmt"
"sync"
"time"
)
var (
counter int
mu sync.Mutex
wg sync.WaitGroup
)
func increment() {
defer wg.Done()
mu.Lock()
defer mu.Unlock()
temp := counter
time.Sleep(1 * time.Millisecond)
counter = temp + 1
}
func main() {
for i := 0; i < 100; i++ {
wg.Add(1)
go increment()
}
wg.Wait()
fmt.Println("Final counter:", counter)
}
此示例中,sync.Mutex保护了counter变量的更新,避免了竞态条件。输出应为Final counter: 100。
使用通道(channel)同步
通道可以用于goroutine间的通信,通过发送和接收操作隐式实现同步。以下示例使用通道控制对共享资源的访问,模拟类似互斥锁的行为。
package main
import (
"fmt"
"sync"
"time"
)
var (
counter int
wg sync.WaitGroup
)
func increment(ch chan struct{}) {
defer wg.Done()
<-ch // 从通道接收,等待许可
temp := counter
time.Sleep(1 * time.Millisecond)
counter = temp + 1
ch <- struct{}{} // 发送回通道,释放许可
}
func main() {
ch := make(chan struct{}, 1)
ch <- struct{}{} // 初始化一个许可
for i := 0; i < 100; i++ {
wg.Add(1)
go increment(ch)
}
wg.Wait()
fmt.Println("Final counter:", counter)
}
此示例使用缓冲通道(容量为1)作为信号量,确保同一时间只有一个goroutine能获取许可并访问counter。输出同样为Final counter: 100。
选择建议
- 互斥锁:适用于简单的共享资源保护,代码直观,性能开销通常较低。
- 通道:适用于更复杂的协调场景,如流水线、任务分发或超时控制。Go的哲学是“不要通过共享内存来通信,而应通过通信来共享内存”。
根据你的需求,如果只是保护共享资源,互斥锁是直接选择;如果需要更灵活的goroutine间协作,通道可能更合适。

