使用Go语言切片在并发编程中可能遇到的问题
- 数据竞争(Data Race):多个协程同时读写切片时,可能导致数据不一致的情况。例如,一个协程在写入切片时,另一个协程同时读取切片,可能读到部分修改的数据。
解决方法
- 使用互斥锁(Mutex):
- 原理:通过
sync.Mutex
来保护对切片的读写操作,确保同一时间只有一个协程可以访问切片。
- 示例代码:
package main
import (
"fmt"
"sync"
)
var (
slice []int
mu sync.Mutex
)
func addToSlice(num int, wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
slice = append(slice, num)
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go addToSlice(i, &wg)
}
wg.Wait()
fmt.Println(slice)
}
- 读写锁(RWMutex):
- 原理:当读操作远多于写操作时,可以使用
sync.RWMutex
。允许多个协程同时进行读操作,但写操作时会独占切片,阻止其他读写操作。
- 示例代码:
package main
import (
"fmt"
"sync"
)
var (
slice []int
rwmu sync.RWMutex
)
func readSlice(index int, wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock()
value := slice[index]
fmt.Printf("Read value: %d at index %d\n", value, index)
rwmu.RUnlock()
}
func writeSlice(num int, wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock()
slice = append(slice, num)
rwmu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go writeSlice(i, &wg)
}
for i := 0; i < len(slice); i++ {
wg.Add(1)
go readSlice(i, &wg)
}
wg.Wait()
}
- 使用通道(Channel):
- 原理:通过通道来安全地传递切片数据,避免直接在多个协程中共享切片。每个协程通过通道接收或发送切片数据,而不是直接读写共享的切片。
- 示例代码:
package main
import (
"fmt"
"sync"
)
func worker(input <-chan int, output chan<- []int, wg *sync.WaitGroup) {
defer wg.Done()
var localSlice []int
for num := range input {
localSlice = append(localSlice, num)
}
output <- localSlice
}
func main() {
var wg sync.WaitGroup
inputChan := make(chan int)
outputChan := make(chan []int)
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(inputChan, outputChan, &wg)
}
go func() {
for i := 0; i < 10; i++ {
inputChan <- i
}
close(inputChan)
}()
go func() {
wg.Wait()
close(outputChan)
}()
var finalSlice []int
for subSlice := range outputChan {
finalSlice = append(finalSlice, subSlice...)
}
fmt.Println(finalSlice)
}
sync包中辅助实现切片并发安全的工具
- sync.Mutex:用于保护对切片的读写操作,通过加锁和解锁确保同一时间只有一个协程可以访问切片。
- sync.RWMutex:适用于读多写少的场景,读操作时允许多个协程同时进行,写操作时独占切片,保证数据一致性。
- sync.WaitGroup:虽然它本身不直接保护切片,但可以用于等待所有协程完成对切片的操作,确保在所有操作完成后再进行后续处理。