【问题标题】:How do I use RWMutex when concurrently modifying a map while iterating over it在迭代地图的同时修改地图时如何使用 RWMutex
【发布时间】:2020-10-31 15:51:56
【问题描述】:

我想使用地图的键向 API 请求某些内容,然后根据 API 的响应更新相应的值。

我的猜测是下面的代码。

或者也许从头开始这种方法,在迭代之前将映射键收集到一个数组中 然后使用数组条目发出请求并修改地图

wg := &sync.WaitGroup{}
wg.Add(len(someMap))

sem := semaphore.NewWeighted(maxWorkers)
ctx := context.TODO()
mutex := &sync.RWMutex{}

mutex.RLock()
for k, v := range someMap {
    mutex.RUnlock()
    go func(k, v) {
        defer wg.Done()

        sem.Acquire(ctx, 1)
        res, err := API.REQUEST(k)
        sem.Release(1)

        if err != nil {
            return
        }

        v.SomeElement = res.SomeElement
        mutex.Lock()
        someMap[k] = v
        mutex.Unlock()
    }(k, v)
    mutex.RLock()
}
mutex.RUnlock()

wg.Wait()

【问题讨论】:

  • 虽然 IIRC 您可以成功锁定范围子句,但这里的问题是您最终可能会或可能不会遍历新键。来自规范:If a map entry is created during iteration, that entry may be produced during the iteration or may be skipped。我只会收集你想要迭代的键,让代码更容易理解
  • 感谢您的回答!使用额外的数组可以大大简化代码(这在 IMO 中更可取)。但我有点不喜欢分配不必要的内存的想法。即使它甚至可以加速代码,因为它会摆脱所有那些 RLock 操作,地图也不是真的“大”。并且不会创建任何条目,因为我只想更新现有条目。
  • 好吧,如果地图不是那么大,那么分配一个键的切片并不是什么大问题。通过网络执行单个 API 调用可能会分配更多数量级。谨防在没有实际数据的情况下进行优化。

标签: loops go concurrency mutex go-map


【解决方案1】:

你正在做的应该工作。但是,请注意,在 runlock 和 rlock 之间,您所做的只是创建一个 goroutine,因此您将有很多 goroutine 等待锁定。先迭代地图再修改更容易阅读和调试。或者,您可以更改地图并拥有map[keytype]struct{value *valuetype},然后您不需要锁定地图,并且无需互斥锁即可运行您的 goroutine。每个 goroutine 都会修改 map[k].value 而不是 map[k]。

【讨论】:

    猜你喜欢
    • 2015-02-05
    • 2021-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 2013-11-21
    • 2011-12-08
    • 1970-01-01
    相关资源
    最近更新 更多