- 可能出现的数据竞争问题:
- 由于Go函数参数传递是值传递,虽然结构体本身是值传递,但结构体内部的指针字段可能会指向共享内存。当多个协程同时调用该函数并通过结构体指针字段访问或修改共享数据时,就可能出现数据竞争。例如,结构体中有一个指向共享切片的指针,多个协程同时对该切片进行读写操作,就会导致数据不一致。
- 使用互斥锁避免数据竞争:
package main
import (
"fmt"
"sync"
)
// 定义共享结构体
type SharedStruct struct {
Data int
}
var mu sync.Mutex
// 接收共享结构体的函数
func processSharedStruct(s *SharedStruct) {
mu.Lock()
defer mu.Unlock()
// 对共享数据进行操作
s.Data++
fmt.Println("Processed data:", s.Data)
}
func main() {
var wg sync.WaitGroup
shared := SharedStruct{Data: 0}
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
processSharedStruct(&shared)
}()
}
wg.Wait()
}
- 解释:
- 定义了一个共享结构体
SharedStruct
,包含一个Data
字段。
- 使用
sync.Mutex
来保护对SharedStruct
数据的访问。在processSharedStruct
函数中,通过mu.Lock()
获取锁,在函数结束时通过defer mu.Unlock()
释放锁,这样确保同一时间只有一个协程能访问和修改SharedStruct
中的数据,避免了数据竞争。
- 使用通道避免数据竞争:
package main
import (
"fmt"
"sync"
)
// 定义共享结构体
type SharedStruct struct {
Data int
}
func processSharedStruct(ch <-chan *SharedStruct, wg *sync.WaitGroup) {
defer wg.Done()
for s := range ch {
// 对共享数据进行操作
s.Data++
fmt.Println("Processed data:", s.Data)
}
}
func main() {
var wg sync.WaitGroup
shared := SharedStruct{Data: 0}
ch := make(chan *SharedStruct)
for i := 0; i < 5; i++ {
wg.Add(1)
go processSharedStruct(ch, &wg)
}
for i := 0; i < 5; i++ {
ch <- &shared
}
close(ch)
wg.Wait()
}
- 解释:
- 同样定义了共享结构体
SharedStruct
。
- 创建一个通道
ch
,类型为*SharedStruct
指针类型。协程从通道中接收结构体指针并操作。
- 在
main
函数中,通过ch <- &shared
将共享结构体指针发送到通道,协程从通道接收并处理。通道本身是线程安全的,通过这种方式,同一时间只有一个协程能处理从通道接收到的共享结构体,从而避免数据竞争。当发送完所有数据后,通过close(ch)
关闭通道,使得协程的for... range
循环结束。