插件窝 干货文章 如何安全地访问和修改Go语言中的线程安全全局变量?

如何安全地访问和修改Go语言中的线程安全全局变量?

sync globalVar func RWMutex 25    来源:    2025-03-16

在Go语言中,访问和修改全局变量时,确保线程安全是非常重要的。Go语言提供了多种机制来保证并发访问的安全性,其中最常用的是使用sync.Mutexsync.RWMutex来保护共享资源。以下是几种常见的方法:

1. 使用 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())
}

2. 使用 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())
}

3. 使用 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())
}

4. 使用通道(Channel)

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:适用于简单的计数器或标志位。
  • 通道(Channel):适用于通过通信来共享内存的场景。

根据具体的应用场景选择合适的并发控制机制,可以有效地保证线程安全。