提升性能并保证公平性的策略
- 分段锁策略
- 思路:将需要保护的资源按照一定规则进行分段,每个分段使用一个独立的Mutex。这样不同的goroutine可以同时访问不同分段的资源,减少锁竞争。例如,假设有一个大的数组资源,我们可以按数组下标范围进行分段。
- 代码示例:
package main
import (
"fmt"
"sync"
)
const numSegments = 10
var segments = make([]sync.Mutex, numSegments)
func getSegmentIndex(key int) int {
return key % numSegments
}
func accessResource(key, value int) {
index := getSegmentIndex(key)
segments[index].Lock()
defer segments[index].Unlock()
// 这里进行对资源的操作,比如假设资源是一个全局数组
// globalArray[key] = value
fmt.Printf("Accessed resource with key %d and value %d\n", key, value)
}
- 读写锁优化(适用于读多写少场景)
- 思路:如果对共享资源的操作主要是读操作,使用
sync.RWMutex
。多个读操作可以同时进行,只有写操作需要独占锁,这样能大大提高并发性能。读操作时不影响其他读操作,只有写操作时才会阻塞读和其他写操作。
- 代码示例:
package main
import (
"fmt"
"sync"
)
var rwMutex sync.RWMutex
var sharedData int
func readData() {
rwMutex.RLock()
defer rwMutex.RUnlock()
fmt.Printf("Read data: %d\n", sharedData)
}
func writeData(newData int) {
rwMutex.Lock()
defer rwMutex.Unlock()
sharedData = newData
fmt.Printf("Wrote data: %d\n", sharedData)
}
- 使用公平锁
- 思路:Go 1.9 引入了公平模式的Mutex。在公平模式下,Mutex 会按照请求的顺序来授予锁,等待时间最长的 goroutine 会优先获得锁,从而保证公平性。在高竞争场景下,公平锁虽然会有一定的性能开销,但能保证所有请求都有机会获取锁。
- 代码示例:
package main
import (
"fmt"
"sync"
"time"
)
var mu sync.Mutex
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 3; i++ {
mu.Lock()
fmt.Printf("Worker %d acquired lock\n", id)
time.Sleep(100 * time.Millisecond)
fmt.Printf("Worker %d released lock\n", id)
mu.Unlock()
}
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
}
- 在上述代码基础上,如果要使用公平锁,可以在Mutex定义后添加如下代码:
var mu sync.Mutex
func init() {
mu = sync.Mutex{}
mu.TryLock() // 触发公平模式初始化
mu.Unlock()
}
- 减少锁的持有时间
- 思路:尽量将不需要锁保护的操作移出临界区,缩短锁的持有时间,从而减少其他goroutine等待锁的时间,提高并发性能。
- 代码示例:
package main
import (
"fmt"
"sync"
)
var mu sync.Mutex
var data int
func process() {
// 计算一些临时值,这些操作不需要锁保护
temp := 10 + 20
mu.Lock()
data = temp
fmt.Printf("Set data to %d\n", data)
mu.Unlock()
// 后续操作如果不需要锁保护,可以继续在这里进行
result := data * 2
fmt.Printf("Calculated result: %d\n", result)
}