在Go语言中,锁用于同步访问共享资源。Go语言提供了两种类型的锁:互斥锁(mutex)和读写锁(RWMutex)。
- 互斥锁(mutex):互斥锁是最基本的锁之一。它通过 Lock() 和 Unlock() 方法来控制对共享资源的并发访问。当一个 goroutine 获取到互斥锁时,其他 goroutine 将被阻塞直到该 goroutine 释放锁为止。互斥锁适合在读写比例不确定的情况下使用。
- 读写锁(RWMutex):读写锁是一种更高效的锁,它允许多个 goroutine 同时读取共享资源,但只允许一个 goroutine 写入共享资源。读写锁通过 RLock()、RUnlock()、Lock() 和 Unlock() 等方法来实现。读写锁适合在读操作远多于写操作的情况下使用,可以减少锁的竞争,提高并发性能。
示例代码:
// 使用互斥锁 var mu sync.Mutex
mu.Lock() // 访问共享资源 mu.Unlock()
// 使用读写锁 var rwMu sync.RWMutex
rwMu.RLock() // 读取共享资源 rwMu.RUnlock()
rwMu.Lock() // 写入共享资源 rwMu.Unlock()
以下是一个使用互斥锁的示例,其中有两个 goroutine 竞争访问一个共享变量 count:
代码语言:javascript复制go复制代码
package main
import (
"fmt"
"sync"
)
var (
count int
mu sync.Mutex
wg sync.WaitGroup
)
func main() {
for i := 0; i < 10; i {
wg.Add(1)
go increment()
}
wg.Wait()
fmt.Println("Final count:", count)
}
func increment() {
mu.Lock()
defer mu.Unlock()
count
wg.Done()
}
在这个示例中,我们首先定义了一个计数器 count 和一个互斥锁 mu。然后,我们启动了 10 个 goroutine,每个 goroutine 都会调用 increment 函数来增加计数器的值。
在 increment 函数中,我们先获取互斥锁 mu,然后对计数器 count 进行加一操作,最后释放互斥锁。使用 defer 语句来确保在函数退出之前一定会释放锁。
通过互斥锁 mu 的加锁和解锁操作,我们可以保证同一时刻只能有一个 goroutine 访问共享变量 count,从而避免了竞态条件(race condition)的出现。
以下是一个使用读写锁的示例,其中有多个 goroutine 同时访问一个共享变量 data:
代码语言:javascript复制go复制代码
package main
import (
"fmt"
"sync"
)
var (
data []int
rwMu sync.RWMutex
wg sync.WaitGroup
)
func main() {
for i := 0; i < 10; i {
wg.Add(1)
if i%2 == 0 {
go write(i)
} else {
go read(i)
}
}
wg.Wait()
}
func read(id int) {
rwMu.RLock()
defer rwMu.RUnlock()
fmt.Printf("Goroutine %d read: %vn", id, data)
wg.Done()
}
func write(id int) {
rwMu.Lock()
defer rwMu.Unlock()
data = append(data, id)
fmt.Printf("Goroutine %d write: %vn", id, data)
wg.Done()
}
在这个示例中,我们首先定义了一个切片 data 和一个读写锁 rwMu。然后,我们启动了 10 个 goroutine,其中偶数编号的 goroutine 调用 write 函数来向 data 切片中写入数据,奇数编号的 goroutine 调用 read 函数来读取 data 切片中的数据。
在 read 函数中,我们先获取读锁 rwMu.RLock(),然后对 data 进行读取操作,最后释放读锁 rwMu.RUnlock()。由于读锁可以被多个 goroutine 同时持有,因此多个 goroutine 可以同时读取数据,不会相互阻塞。
在 write 函数中,我们先获取写锁 rwMu.Lock(),然后对 data 进行写入操作,最后释放写锁 rwMu.Unlock()。由于写锁只能被一个 goroutine 持有,因此多个 goroutine 尝试同时写入数据时会相互阻塞,直到当前持有写锁的 goroutine 释放锁为止。
通过读写锁 rwMu 的读锁和写锁操作,我们可以保证在写操作时不会有其他 goroutine 对 data 进行读或写操作,而在读操作时可以允许多个 goroutine 同时读取 data,从而提高了程序的并发性能。