一、使用sync.Mutex或sync.RWMutex进行并发安全访问
当多个协程并发访问共享数据时,需要确保数据访问的安全性。sync.Mutex和sync.RWMutex提供了互斥锁和读写锁,用于在访问共享资源之前进行锁定,以避免数据竞争。
sync.Mutex
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var mutex sync.Mutex
var data int
var num = 20000
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < num; i++ {
// 写操作,使用互斥锁保护数据
mutex.Lock() //上了锁是还可以读的
data++
mutex.Unlock()
}
}()
go func() {
defer wg.Done()
for i := 0; i < num; i++ {
// 写操作,使用互斥锁保护数据
mutex.Lock() //上了锁是还可以读的
data++
mutex.Unlock()
}
}()
wg.Wait()
fmt.Println(data)
fmt.Println("在并发的情况下,当num数大到一定程度,如果不加锁就会出现最终运算结果不正确的情况")
}
ync.RWMutex
sync.RWMutex是Go语言提供的一个基础同步原语,它是Reader/Writer Mutual Exclusion Lock的缩写,通常被称为"读写锁"。读写锁允许多个读锁同时拥有者,但在任何时间点只允许一个写锁拥有者,或者没有锁拥有者。
sync.RWMutex提供了以下方法:
type RWMutex
// 获取写锁,有读锁或者写锁被其他goroutine使用则阻塞等待
func (rw *RWMutex) Lock()
// 尝试获取写锁,获取到则返回true,没有获取到则为false
func (rw *RWMutex) TryLock() bool
// 释放写锁
func (rw *RWMutex) Unlock()
// 获取读锁,
func (rw *RWMutex) RLock()
// 尝试获取读锁,获取到则返回true,没有获取到则为false
func (rw *RWMutex) TryRLock() bool
// 释放读锁
func (rw *RWMutex) RUnlock()
// 返回Locker
func (rw *RWMutex) RLocker() Locker
注意:
使用RWMutex的时候,一旦调用了Lock方法,就不能再把该锁复制到其他地方使用,否则可能会出现各种问题。这是由于锁的状态(被哪个协程持有,是否已经被锁定等)是存储在RWMutex的结构体中,如果复制了RWMutex,那么复制后的RWMutex就会有一个全新的状态,锁的行为就会变得不可预测。
RWMutex和Mutex一样,一旦有了Lock调用就不能到处copy了,否则出现各种问题。
sync.RWMutex
包含两个主要的方法:
-
RLock()
:用于读锁,多个goroutine可以同时调用此方法获取读锁。 -
RUnlock()
:用于释放读锁。 -
Lock()
:用于写锁,当有goroutine调用此方法时,其他goroutine的读锁和写锁请求都会被阻塞,直到写锁被释放。 -
Unlock()
:用于释放写锁。
package main
import (
"fmt"
"strconv"
"sync"
"time"
)
func main() {
fmt.Println("通过使用 sync.RWMutex,我们可以确保即使在高并发的情况下,对值的读写也是安全的")
fmt.Println("Lock与RLock配合,使用使得写入的时候阻塞不给读取,等写完了再读")
var (
mu sync.RWMutex // 创建一个RWMutex实例
count int
)
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
// 启动一些goroutine来增加count的值
for j := 0; j < 100; j++ {
mu.Lock() // 获取写锁
fmt.Println("获取写锁Lock--其他goroutine的读锁和写锁请求都会被阻塞,直到写锁被释放")
time.Sleep(3000 * time.Millisecond)
count++
fmt.Println()
fmt.Println("---------------写锁被释放----------")
mu.Unlock() // 释放写锁
}
}()
time.Sleep(time.Millisecond)
go func() {
defer wg.Done()
// 启动一些goroutine来读取count的值
for j := 0; j < 100; j++ {
mu.RLock() // 获取读锁
fmt.Println("获取读锁RLock--多个goroutine可以同时调用此方法获取读锁count=" + strconv.Itoa(count))
mu.RUnlock() // 释放读锁
}
}()
go func() {
defer wg.Done()
// 启动一些goroutine来读取count的值
for j := 0; j < 100; j++ {
mu.RLock() // 获取读锁
fmt.Println("获取读锁RLock--多个goroutine可以同时调用此方法获取读锁count=" + strconv.Itoa(count))
mu.RUnlock() // 释放读锁
}
}()
wg.Wait()
fmt.Println("Final count:", count)
}