在Golang中,使用sync.Mutex对map[string]int进行竞争控制。

huangapple go评论80阅读模式
英文:

Golang race with sync.Mutex on map[string]int

问题

我有一个简单的包,在程序运行期间用于记录统计信息,但是我发现go run -race命令显示其中存在竞争条件。看着程序,我不确定在每次读写都受到互斥锁保护的情况下,如何可能存在竞争条件。有人可以解释一下吗?

package counters

import "sync"

type single struct {
    mu     sync.Mutex
    values map[string]int64
}

// 全局计数器对象
var counters = single{
    values: make(map[string]int64),
}

// 获取给定计数器的值
func Get(key string) int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    return counters.values[key]
}

// 增加给定计数器名称的值
func Incr(key string) int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    counters.values[key]++ // 竞争条件
    return counters.values[key]
}

// 所有计数器
func All() map[string]int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    return counters.values // 在上面的写入期间运行
}

我像这样使用该包:

counters.Incr("foo")
counters.Get("foo")
英文:

I have a simple package I am using to log stats during a program run and I found that go run -race says there is a race condition in it. Looking at the program I'm not sure how I can have a race condition when every read and write is protected by a mutex. Can someone explain this?

package counters

import "sync"

type single struct {
	mu     sync.Mutex
	values map[string]int64
}

// Global counters object
var counters = single{
	values: make(map[string]int64),
}

// Get the value of the given counter
func Get(key string) int64 {
	counters.mu.Lock()
	defer counters.mu.Unlock()
	return counters.values[key]
}

// Incr the value of the given counter name
func Incr(key string) int64 {
	counters.mu.Lock()
	defer counters.mu.Unlock()
	counters.values[key]++        // Race condition
	return counters.values[key]
}

// All the counters
func All() map[string]int64 {
	counters.mu.Lock()
	defer counters.mu.Unlock()
	return counters.values        // running during write above
}

I use the package like so:

counters.Incr("foo")
counters.Get("foo")

答案1

得分: 3

这里需要一个最小完整可验证的示例,但我认为你的问题出在All()函数中:

// 所有计数器
func All() map[string]int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    return counters.values // 在上面的写操作期间运行
}

这个函数返回一个map,它没有对其进行复制,因此可以在互斥锁的保护之外访问它。

英文:

A Minimal Complete Verifiable Example would be useful here, but I think your problem is in All():

// All the counters
func All() map[string]int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    return counters.values        // running during write above
}

This returns a map which does not make a copy of it, so it can be accessed outside the protection of the mutex.

答案2

得分: 2

All方法返回底层的映射并释放锁,因此使用该映射的代码可能会出现数据竞争。

你应该返回映射的副本:

func All() map[string]int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    m := make(map[string]int64)
    for k, v := range counters.values {
        m[k] = v
    }
    return m    
}

或者不使用All方法。

英文:

All returns the underlying map and the releases the lock, so the code using the map will have a data race.

You should return a copy of the map:

func All() map[string]int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    m := make(map[string]int64)
    for k, v := range counters.values {
        m[k] = v
    }
    return m    
}

Or not have an All method.

huangapple
  • 本文由 发表于 2016年12月15日 07:19:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/41153912.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定