Golang并行映射访问范围

时间:2015-12-04 04:03:10

标签: data-structures go concurrency

我有一张地图,其中包含需要在清除地图之前释放的对象。当我走过它时,我很想迭代地图并删除/释放对象。

这是一个模拟示例 https://play.golang.org/p/kAtPoUgMsq

由于迭代地图的唯一方法是通过范围,我如何同步多个生产者和多个消费者?

我不想读取锁定地图,因为这样会在迭代过程中删除/修改密钥。

3 个答案:

答案 0 :(得分:6)

有很多方法可以清除createWindow()的内容,而不需要进行有效的地图访问。适用于您的应用程序取决于它正在做什么。

0)只需在处理地图时锁定地图。如果地图不是太大,或者你有一些延迟容忍度,它可以快速完成工作(就你花费的时间而言),你可以继续思考其他的东西。如果以后出现问题,那么你可以回过头来解决问题。

1)将对象或指针复制出来并在握住锁定的同时清除地图,然后在后台释放对象。如果问题在于释放本身的缓慢会使锁保持很长时间,这就是简单的解决方法。

2)如果有效读取基本上都很重要,请使用map。这使您可以用一个新的不同的地图完全替换一个地图。如果写入基本上是工作负载的0%,则高效读取会平衡每次更改时创建新映射的成本。这种情况很少发生,但它发生了,例如,atomic.Value有一个以这种方式管理的全球类型地图。

3)如果这些都不能满足您的需求,请调整存储数据的方式(例如,分割地图)。用你自己的16个地图和哈希键替换你的地图,以决定一个东西属于哪个地图,然后你可以一次锁定一个碎片,进行清理或任何其他写入。

还有发布和使用之间的竞争问题:goroutine A从地图中获取内容,B清除地图并释放内容,A使用已发布的内容。

一种策略是在您使用或释放​​它时锁定每个值;那么你需要锁而不是全局锁。

另一种方法是,如果他们知道并且不是灾难性的话,就能容忍种族的后果;例如,其文档明确允许对encoding/gob进行并发访问,因此关闭正在使用的连接可能会导致对其的请求出错,但不会导致应用程序行为未定义。你必须确定你知道自己要了解的是什么,因为many benign-seeming races aren't

最后,也许您的应用程序已经确保没有释放使用中的对象,例如对象上有一个安全维护的引用计数,只释放未使用的对象。然后,当然,你不必担心。

试图以某种方式用通道替换这些锁可能很诱人,但我没有看到任何收益。当你可以设计你的应用程序主要是考虑进程之间的通信而不是共享数据时,它是很好,但是当你有共享数据时,除此之外没有任何用处。排除对共享数据的不安全访问是锁的用途。

答案 1 :(得分:4)

您没有陈述所有要求(例如,多个对象的发布可以同时发生等),但我能想到的最简单的解决方案是删除元素并为每个被删除的元素启动发布goroutine:

for key := range keysToRemove {
    if v, ok := m[k]; ok {
        delete(m, k)
        go release(k, v)
    }
}

答案 2 :(得分:2)

2017年8月更新(golang 1.9

您现在Map包中有一个新的sync类型是一个并发地图,其中包含摊销常数时间加载,存储和删除。
多个goroutine可以安全地同时调用Map的方法。

原始回答2016年11月

  

我不想读锁地图

这是有道理的,因为从地图中删除被认为是写操作,并且必须与所有其他读写一起序列化。这意味着写锁定以完成删除。 (来源:this answer

假设最糟糕的情况(多个作者和读者),您可以查看orcaman/concurrent-map的实现,其Remove() method使用多个$scope.generatePDF = function(script) { console.log("Insert into " + script); }; 因为,以避免锁定瓶颈,这个并发的地图潜水到几个(sync.RWMutex)地图碎片 这比只使用一个SHARD_COUNT as in this example快。