竞态条件指的是多个协程在同时访问和修改某个共享变量时,最终的结果依赖于协程执行的顺序。为了避免竞态条件,Go语言提供了两种方法:互斥锁(Mutex)和通道(Channel)。
互斥锁是Go语言中最基本的同步原语之一,通过 Lock() 和 Unlock() 方法来控制多个协程对共享变量的访问。协程在访问共享变量之前需要先获得互斥锁,操作完成后再释放锁。示例如下:
```
var counter int
var mutex sync.Mutex
func increment() {
mutex.Lock()
counter++
mutex.Unlock()
}
```
通道是Go语言中实现并发通信和同步的另一个重要机制,它可以用于协程之间传递数据和控制执行顺序。通过使用通道,我们可以避免显式地使用互斥锁来保护共享变量。示例如下:
```
var counter int
var ch = make(chan int)
func increment() {
ch <- 1 // 发送一个计数
}
func main() {
go increment()
<-ch // 等待一个计数
fmt.Println(counter)
}
```
协程局部变量
除了共享变量,协程还可以访问局部变量。不同于共享变量,协程对局部变量的访问是安全的,每个协程都拥有自己独立的栈空间,因此不存在竞态条件问题。这使得协程可以更加灵活地处理任务,不需要额外的同步机制。下面是一个使用协程实现并行计算的示例:
```
func compute(data []int, ch chan int) {
result := 0
for _, value := range data {
result += value
}
ch <- result
}
func main() {
data := []int{1, 2, 3, 4, 5}
ch := make(chan int, len(data))
for i := 0; i < len(data); i++ {
go compute(data[i:i+1], ch)
}
total := 0
for i := 0; i < len(data); i++ {
total += <-ch
}
fmt.Println(total)
}
```
在这个示例中,我们将计算任务分解成多个小的计算单元,每个协程只处理一个小的计算单元。通过使用通道进行数据传递,主协程可以等待所有计算结果的返回,并汇总得到最终结果。