【问题标题】:Go goroutine lock and unlockGo goroutine 锁定和解锁
【发布时间】:2016-06-27 17:13:15
【问题描述】:

我一直在阅读有关 goroutines 和同步包的信息,我的问题是……在读取不同 goroutine 上的数据时,我是否总是需要锁定解锁?

例如,我的服务器上有一个变量

config := make(map[string]string)

然后在不同的 goroutines 上我想从 config 中读取。不使用同步阅读是否安全?

我猜写作需要使用同步包来完成。但我不确定是否阅读

例如我有一个简单的内存缓存系统

type Cache interface {
    Get(key string) interface{}
    Put(key string, expires int64, value interface{})
}

// MemoryCache represents a memory type of cache
type MemoryCache struct {
    c map[string]*MemoryCacheValue
    rw sync.RWMutex
}

// MemoryCacheValue represents a memory cache value
type MemoryCacheValue struct {
    value interface{}
    expires int64
}

// NewMemoryCache creates a new memory cache
func NewMemoryCache() Cache {
    return &MemoryCache{
        c: make(map[string]*MemoryCacheValue),
    }
}

// Get stores something into the cache
func (m *MemoryCache) Get(key string) interface{} {
    if v, ok := m.c[key]; ok {
        return v
    }
    return nil
}

// Put retrieves something from the cache
func (m *MemoryCache) Put(key string, expires int64, value interface{}) {
    m.rw.Lock()
    m.c[key] = &MemoryCacheValue{
        value,
        time.Now().Unix() + expires,
    }
    m.rw.Unlock()
}

我在这里是安全的,还是我只想阅读时仍需要锁定解锁?

【问题讨论】:

    标签: go synchronization


    【解决方案1】:

    您正在潜入比赛条件的世界。基本的经验法则是,如果 ANY 例程写入或更改可以或被任意数量的其他协程/线程读取(或也写入)的一段数据,您需要有某种同步系统。

    例如,假设您有那张地图。它有 ["Joe"] = "Smith" 和 ["Sean"] = "Howard"。一个 goroutine 想要读取 ["Joe"] 的值。另一个例程是将 ["Joe"] 更新为 "Cooper"。第一个 goroutine 读取哪个值?取决于哪个 goroutine 先获取数据。这就是竞争条件,行为是未定义且不可预测的。

    控制该访问的最简单方法是使用sync.Mutex。在您的情况下,由于某些例程只需要读取而不需要写入,因此您可以改用 sync.RWMutex (主要区别在于 RWMutex 允许读取任意数量的线程,只要没有尝试写入)。您可以使用如下结构将其烘焙到地图中:

    type MutexMap struct {
        m map[string]string
        *sync.RWMutex
    }
    

    然后,在需要从地图读取的例程中,您会这样做:

    func ReadSomething(o MutexMap, key string) string {
        o.RLock() // lock for reading, blocks until the Mutex is ready
        defer o.RUnlock() // make SURE you do this, else it will be locked permanently
        return o.m[key]
    }
    

    然后写:

    func WriteSomething(o MutexMap, key, value string) {
        o.Lock() // lock for writing, blocks until the Mutex is ready
        defer o.Unlock() // again, make SURE you do this, else it will be locked permanently
        o.m[key] = value
    }
    

    请注意,如果需要,这两个都可以写成结构的方法,而不是函数。


    您也可以使用渠道来解决此问题。您创建一个在 goroutine 中运行的控制器结构,并通过通道向它发出请求。示例:

    package main
    
    import "fmt"
    
    type MapCtrl struct {
        m       map[string]string
        ReadCh  chan chan map[string]string
        WriteCh chan map[string]string
        QuitCh  chan struct{}
    }
    
    func NewMapController() *MapCtrl {
        return &MapCtrl{
            m:       make(map[string]string),
            ReadCh:  make(chan chan map[string]string),
            WriteCh: make(chan map[string]string),
            QuitCh:  make(chan struct{}),
        }
    }
    
    func (ctrl *MapCtrl) Control() {
        for {
            select {
            case r := <-ctrl.ReadCh:
                fmt.Println("Read request received")
                retmap := make(map[string]string)
                for k, v := range ctrl.m { // copy map, so it doesn't change in place after return
                    retmap[k] = v
                }
                r <- retmap
            case w := <-ctrl.WriteCh:
                fmt.Println("Write request received with", w)
                for k, v := range w {
                    ctrl.m[k] = v
                }
            case <-ctrl.QuitCh:
                fmt.Println("Quit request received")
                return
            }
        }
    }
    
    func main() {
        ctrl := NewMapController()
        defer close(ctrl.QuitCh)
        go ctrl.Control()
    
        m := make(map[string]string)
        m["Joe"] = "Smith"
        m["Sean"] = "Howard"
        ctrl.WriteCh <- m
    
        r := make(chan map[string]string, 1)
        ctrl.ReadCh <- r
        fmt.Println(<-r)
    }
    

    Runnable version

    【讨论】:

    • 我会摆脱假比赛的例子,因为它既不适用于地图的 go 实现,也不适用于字符串,而且有点令人困惑。在第一个示例中,您还混淆了 om,同时将 Mutex 方法引用为嵌入字段,但将其定义为 mutex
    • 根据建议更正。
    猜你喜欢
    • 2019-09-07
    • 1970-01-01
    • 1970-01-01
    • 2021-06-21
    • 2021-11-23
    • 2016-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多