如何在Go中通过锁定实现映射的线程安全包装器?

如何在Go中通过锁定实现映射的线程安全包装器?,go,thread-safety,mutex,panic,Go,Thread Safety,Mutex,Panic,我试图将一个通用映射(使用interface{}作为键和值)包装为内存中的键值存储,我将其命名为MemStore。但是它不是线程安全的,尽管我使用了sync.RWMutex来锁定对底层映射的访问。我确实验证了它在从一个goroutine使用时可以正常工作。但是,只有两个并发goroutine访问它会导致死机:运行时错误:无效内存地址或零指针取消引用 导致此问题的原因是什么?在Go中实现线程安全的正确方法是什么?虽然在本例中,与map交互的单个goroutine的通道可以工作,但我特别寻找一种与显

我试图将一个通用映射(使用
interface{}
作为键和值)包装为内存中的键值存储,我将其命名为
MemStore
。但是它不是线程安全的,尽管我使用了
sync.RWMutex
来锁定对底层映射的访问。我确实验证了它在从一个goroutine使用时可以正常工作。但是,只有两个并发goroutine访问它会导致
死机:运行时错误:无效内存地址或零指针取消引用

导致此问题的原因是什么?在Go中实现线程安全的正确方法是什么?虽然在本例中,与map交互的单个goroutine的通道可以工作,但我特别寻找一种与显式锁定一起工作的解决方案。 文件keyval.go:

package keyval

import "sync"

type MemStore struct {
    data map[interface{}]interface{}
    mutex sync.RWMutex
}

func NewMemStore() (MemStore) {
    m := MemStore{
        data: make(map[interface{}]interface{}),
        // mutex does not need initializing
    }
    return m
}

func (m MemStore) Set(key interface{}, value interface{}) (err error) {
    m.mutex.Lock()
    defer m.mutex.Unlock()

    if value != nil {
        m.data[key] = value
    } else {
        delete(m.data, key);
    }

    return nil
}
文件keyval_test.go:

package keyval

import "testing"

func setN(store Store, N int, done chan<- struct{}) {
    for i := 0; i < N; i++ {
        store.Set(i, -i)
    }
    done <- struct{}{}
}

func BenchmarkMemStore(b *testing.B) {
    store := NewMemStore()
    done := make(chan struct{})
    b.ResetTimer()
    go setN(store, b.N, done)
    go setN(store, b.N, done)
    <-done
    <-done
}

关于指针与接收器值的规则是,值方法 可以对指针和值调用,但指针方法只能 在指针上调用。这是因为指针方法可以修改 接受者在值的副本上调用它们会导致 要放弃的修改

要明显修改
MemStore
struct
变量
mutex
字段,请使用指针接收器。您正在修改对其他go例程不可见的副本。比如说,

文件
keyval.go

package keyval

import "sync"

type MemStore struct {
    data  map[interface{}]interface{}
    mutex sync.RWMutex
}

func NewMemStore() *MemStore {
    m := &MemStore{
        data: make(map[interface{}]interface{}),
        // mutex does not need initializing
    }
    return m
}

func (m *MemStore) Set(key interface{}, value interface{}) (err error) {
    m.mutex.Lock()
    defer m.mutex.Unlock()

    if value != nil {
        m.data[key] = value
    } else {
        delete(m.data, key)
    }

    return nil
}
package keyval

import "testing"

func setN(store *MemStore, N int, done chan<- struct{}) {
    for i := 0; i < N; i++ {
        store.Set(i, -i)
    }
    done <- struct{}{}
}

func BenchmarkMemStore(b *testing.B) {
    store := NewMemStore()
    done := make(chan struct{})
    b.ResetTimer()
    go setN(store, b.N, done)
    go setN(store, b.N, done)
    <-done
    <-done
}
文件
keyval\u test.go

package keyval

import "sync"

type MemStore struct {
    data  map[interface{}]interface{}
    mutex sync.RWMutex
}

func NewMemStore() *MemStore {
    m := &MemStore{
        data: make(map[interface{}]interface{}),
        // mutex does not need initializing
    }
    return m
}

func (m *MemStore) Set(key interface{}, value interface{}) (err error) {
    m.mutex.Lock()
    defer m.mutex.Unlock()

    if value != nil {
        m.data[key] = value
    } else {
        delete(m.data, key)
    }

    return nil
}
package keyval

import "testing"

func setN(store *MemStore, N int, done chan<- struct{}) {
    for i := 0; i < N; i++ {
        store.Set(i, -i)
    }
    done <- struct{}{}
}

func BenchmarkMemStore(b *testing.B) {
    store := NewMemStore()
    done := make(chan struct{})
    b.ResetTimer()
    go setN(store, b.N, done)
    go setN(store, b.N, done)
    <-done
    <-done
}