在Go语言中,访问和修改全局变量时,确保线程安全是非常重要的。Go语言提供了多种机制来保证并发访问的安全性,其中最常用的是使用sync.Mutex
或sync.RWMutex
来保护共享资源。以下是几种常见的方法:
sync.Mutex
sync.Mutex
是一个互斥锁,用于保护共享资源,确保同一时间只有一个 goroutine 可以访问该资源。
package main
import (
"fmt"
"sync"
)
var (
globalVar int
mutex sync.Mutex
)
func increment() {
mutex.Lock()
defer mutex.Unlock()
globalVar++
}
func getValue() int {
mutex.Lock()
defer mutex.Unlock()
return globalVar
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final value:", getValue())
}
sync.RWMutex
sync.RWMutex
是一个读写锁,允许多个 goroutine 同时读取共享资源,但在写入时只允许一个 goroutine 访问。
package main
import (
"fmt"
"sync"
)
var (
globalVar int
rwMutex sync.RWMutex
)
func increment() {
rwMutex.Lock()
defer rwMutex.Unlock()
globalVar++
}
func getValue() int {
rwMutex.RLock()
defer rwMutex.RUnlock()
return globalVar
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final value:", getValue())
}
sync/atomic
包对于简单的计数器或标志位,可以使用 sync/atomic
包提供的原子操作来保证线程安全。
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var (
globalVar int64
)
func increment() {
atomic.AddInt64(&globalVar, 1)
}
func getValue() int64 {
return atomic.LoadInt64(&globalVar)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final value:", getValue())
}
Go语言提倡使用通道来在 goroutine 之间传递数据,这样可以避免直接共享内存,从而减少竞态条件的发生。
package main
import (
"fmt"
"sync"
)
var (
globalVar int
ch = make(chan int)
)
func increment() {
ch <- 1
}
func getValue() int {
return globalVar
}
func main() {
var wg sync.WaitGroup
go func() {
for {
select {
case val := <-ch:
globalVar += val
}
}
}()
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final value:", getValue())
}
sync.Mutex
:适用于需要互斥访问的场景。sync.RWMutex
:适用于读多写少的场景。sync/atomic
:适用于简单的计数器或标志位。根据具体的应用场景选择合适的并发控制机制,可以有效地保证线程安全。