# 优化 Go 中的 map 并发存取

88250 · · 17301 次点击 · · 开始浏览

Catena （时序存储引擎）中有一个函数的实现备受争议，它从 map 中根据指定的 `name` 获取一个 `metricSource`。每一次插入操作都会至少调用一次这个函数，现实场景中该函数调用更是频繁，并且是跨多个协程的，因此我们必须要考虑同步。

```var source *memorySource
var present bool

p.lock.Lock() // lock the mutex
defer p.lock.Unlock() // unlock the mutex at the end

if source, present = p.sources[name]; !present {
// The source wasn't found, so we'll create it.
source = &memorySource{
name: name,
metrics: map[string]*memoryMetric{},
}

// Insert the newly created *memorySource.
p.sources[name] = source
}```

```var source *memorySource
var present bool

if source, present = p.sources[name]; !present { // added this line
// The source wasn't found, so we'll create it.

p.lock.Lock() // lock the mutex
defer p.lock.Unlock() // unlock at the end

if source, present = p.sources[name]; !present {
source = &memorySource{
name: name,
metrics: map[string]*memoryMetric{},
}

// Insert the newly created *memorySource.
p.sources[name] = source
}
// if present is true, then another goroutine has already inserted
// the element we want, and source is set to what we want.

} // added this line

// Note that if the source was present, we avoid the lock completely!```

John Potocny 建议移除 `defer`，因为会延误解锁时间（要在整个函数返回时才解锁），下面给出一个“终极”版本：

```var source *memorySource
var present bool

if source, present = p.sources[name]; !present {
// The source wasn't found, so we'll create it.

p.lock.Lock() // lock the mutex
if source, present = p.sources[name]; !present {
source = &memorySource{
name: name,
metrics: map[string]*memoryMetric{},
}

// Insert the newly created *memorySource.
p.sources[name] = source
}
p.lock.Unlock() // unlock the mutex
}

// Note that if the source was present, we avoid the lock completely!```

9,800,000 插入/秒！改了 4 行提升到 7 倍啊！！有木有！！！！

```var source *memorySource
var present bool

p.lock.RLock()
if source, present = p.sources[name]; !present {
// The source wasn't found, so we'll create it.
p.lock.RUnlock()
p.lock.Lock()
if source, present = p.sources[name]; !present {
source = &memorySource{
name: name,
metrics: map[string]*memoryMetric{},
}

// Insert the newly created *memorySource.
p.sources[name] = source
}
p.lock.Unlock()
} else {
p.lock.RUnlock()
}```

---- EOF ----

7 回复  |  直到 2000-01-01 00:00:00

• 请尽量让自己的回复能够对别人有帮助
• 支持 Markdown 格式, **粗体**、~~删除线~~、``单行代码``
• 支持 @ 本站用户；支持表情（输入 : 提示），见 Emoji cheat sheet
• 图片支持拖拽、截图粘贴等方式上传

Catena （时序存储引擎）中有一个函数的实现备受争议，它从 map 中根据指定的 `name` 获取一个 `metricSource`。每一次插入操作都会至少调用一次这个函数，现实场景中该函数调用更是频繁，并且是跨多个协程的，因此我们必须要考虑同步。

```var source *memorySource
var present bool

p.lock.Lock() // lock the mutex
defer p.lock.Unlock() // unlock the mutex at the end

if source, present = p.sources[name]; !present {
// The source wasn't found, so we'll create it.
source = &memorySource{
name: name,
metrics: map[string]*memoryMetric{},
}

// Insert the newly created *memorySource.
p.sources[name] = source
}```

```var source *memorySource
var present bool

if source, present = p.sources[name]; !present { // added this line
// The source wasn't found, so we'll create it.

p.lock.Lock() // lock the mutex
defer p.lock.Unlock() // unlock at the end

if source, present = p.sources[name]; !present {
source = &memorySource{
name: name,
metrics: map[string]*memoryMetric{},
}

// Insert the newly created *memorySource.
p.sources[name] = source
}
// if present is true, then another goroutine has already inserted
// the element we want, and source is set to what we want.

} // added this line

// Note that if the source was present, we avoid the lock completely!```

John Potocny 建议移除 `defer`，因为会延误解锁时间（要在整个函数返回时才解锁），下面给出一个“终极”版本：

```var source *memorySource
var present bool

if source, present = p.sources[name]; !present {
// The source wasn't found, so we'll create it.

p.lock.Lock() // lock the mutex
if source, present = p.sources[name]; !present {
source = &memorySource{
name: name,
metrics: map[string]*memoryMetric{},
}

// Insert the newly created *memorySource.
p.sources[name] = source
}
p.lock.Unlock() // unlock the mutex
}

// Note that if the source was present, we avoid the lock completely!```

9,800,000 插入/秒！改了 4 行提升到 7 倍啊！！有木有！！！！

```var source *memorySource
var present bool

p.lock.RLock()
if source, present = p.sources[name]; !present {
// The source wasn't found, so we'll create it.
p.lock.RUnlock()
p.lock.Lock()
if source, present = p.sources[name]; !present {
source = &memorySource{
name: name,
metrics: map[string]*memoryMetric{},
}

// Insert the newly created *memorySource.
p.sources[name] = source
}
p.lock.Unlock()
} else {
p.lock.RUnlock()
}```

---- EOF ----