同步机制确保并发安全
- 使用互斥锁(
sync.Mutex
):
- 设计思路:互斥锁可以保证同一时间只有一个
goroutine
能够访问共享结构体Data
,从而避免读写冲突。无论读操作还是写操作,都需要先获取锁,操作完成后释放锁。
- 代码示例:
package main
import (
"fmt"
"sync"
)
type Data struct {
// 假设包含一个切片和一个映射
slice []int
mapData map[string]int
mu sync.Mutex
}
func (d *Data) updateSlice(newValue int) {
d.mu.Lock()
defer d.mu.Unlock()
d.slice = append(d.slice, newValue)
}
func (d *Data) getSlice() []int {
d.mu.Lock()
defer d.mu.Unlock()
return d.slice
}
func main() {
var wg sync.WaitGroup
data := Data{
slice: make([]int, 0),
mapData: make(map[string]int),
}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
data.updateSlice(n)
}(i)
}
wg.Wait()
fmt.Println(data.getSlice())
}
- 使用读写锁(
sync.RWMutex
):
- 设计思路:读写锁适用于读操作远远多于写操作的场景。读操作可以同时进行,而写操作需要独占锁。这样可以提高读操作的并发性能。
- 代码示例:
package main
import (
"fmt"
"sync"
)
type Data struct {
// 假设包含一个切片和一个映射
slice []int
mapData map[string]int
rwmu sync.RWMutex
}
func (d *Data) updateSlice(newValue int) {
d.rwmu.Lock()
defer d.rwmu.Unlock()
d.slice = append(d.slice, newValue)
}
func (d *Data) getSlice() []int {
d.rwmu.RLock()
defer d.rwmu.RUnlock()
return d.slice
}
func main() {
var wg sync.WaitGroup
data := Data{
slice: make([]int, 0),
mapData: make(map[string]int),
}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
data.updateSlice(n)
}(i)
}
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(data.getSlice())
}()
}
wg.Wait()
}
内存管理优化结构体设计
- 预分配内存:
- 设计思路:在结构体初始化时,预先分配足够的内存空间,避免在运行过程中频繁地分配内存。对于切片,可以使用
make
函数指定初始容量;对于映射,可以估计初始元素数量进行初始化。
- 代码示例:
type Data struct {
slice []int
mapData map[string]int
mu sync.Mutex
}
func NewData() *Data {
// 预分配切片容量为100,映射初始元素数量为50
return &Data{
slice: make([]int, 0, 100),
mapData: make(map[string]int, 50),
}
}
- 对象池(
sync.Pool
):
- 设计思路:对于一些频繁创建和销毁的结构体或其内部元素,可以使用
sync.Pool
来缓存对象,减少内存分配和垃圾回收的压力。
- 代码示例:
package main
import (
"fmt"
"sync"
)
type SmallData struct {
value int
}
var smallDataPool = sync.Pool{
New: func() interface{} {
return &SmallData{}
},
}
type Data struct {
subDataList []*SmallData
mu sync.Mutex
}
func (d *Data) addSubData() {
d.mu.Lock()
defer d.mu.Unlock()
data := smallDataPool.Get().(*SmallData)
d.subDataList = append(d.subDataList, data)
}
func (d *Data) releaseSubData() {
d.mu.Lock()
defer d.mu.Unlock()
for _, data := range d.subDataList {
smallDataPool.Put(data)
}
d.subDataList = nil
}
结构体字段动态增长和收缩的内存管理策略
- 切片动态增长:
- 设计思路:使用
append
函数来动态增长切片。append
函数在容量不足时会自动重新分配内存,复制原有数据,并扩大容量。可以通过预分配合适的初始容量来减少重新分配的次数。
- 代码示例:已在上述代码中体现,如
d.slice = append(d.slice, newValue)
。
- 切片动态收缩:
- 设计思路:可以通过重新切片来缩小切片的大小,释放不需要的内存。但要注意,Go语言的垃圾回收机制会自动回收不再使用的内存,所以一般不需要手动释放内存,只需要确保不再持有对无用数据的引用。
- 代码示例:
func (d *Data) shrinkSlice() {
d.mu.Lock()
defer d.mu.Unlock()
if len(d.slice) > 0 {
d.slice = d.slice[:len(d.slice)-1]
}
}
- 映射动态增长和收缩:
- 设计思路:映射在添加新键值对时会自动增长。对于收缩,可以删除不再需要的键值对,
delete
函数会从映射中移除指定的键值对,使该内存可被垃圾回收。
- 代码示例:
func (d *Data) addToMap(key string, value int) {
d.mu.Lock()
defer d.mu.Unlock()
d.mapData[key] = value
}
func (d *Data) removeFromMap(key string) {
d.mu.Lock()
defer d.mu.Unlock()
delete(d.mapData, key)
}