Data structures 具有范围的Golang并发地图访问

Data structures 具有范围的Golang并发地图访问,data-structures,go,concurrency,Data Structures,Go,Concurrency,我有一张地图,上面有需要在清除地图之前释放的对象。我很想在地图上迭代并在遍历时删除/释放对象 下面是一个模型示例 由于迭代映射的唯一方法是通过范围,因此如何同步多个生产者和多个消费者 我不想阅读锁定地图,因为这将使在迭代过程中无法删除/修改密钥。您没有说明所有要求(例如,是否可以同时释放多个对象等)但我能想到的最简单的解决方案是删除元素,并为每个删除的元素启动发布goroutine: for key := range keysToRemove { if v, ok := m[k]; o

我有一张地图,上面有需要在清除地图之前释放的对象。我很想在地图上迭代并在遍历时删除/释放对象

下面是一个模型示例

由于迭代映射的唯一方法是通过范围,因此如何同步多个生产者和多个消费者


我不想阅读锁定地图,因为这将使在迭代过程中无法删除/修改密钥。

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

for key := range keysToRemove {
    if v, ok := m[k]; ok {
        delete(m, k)
        go release(k, v)
    }
}
2017年8月更新()

现在,包中有一个新类型,它是一个并发映射,具有摊销的固定时间加载、存储和删除。
多个goroutine同时调用Map的方法是安全的


原始答复2016年11月

我不想看地图

这是有道理的,因为从映射中删除被视为写入操作,并且必须与所有其他读取和写入一起序列化。这意味着需要一个写锁来完成删除。(来源:)

假设出现最坏的情况(多个写入程序和读取器),您可以查看的实现,它具有一个使用多个
sync.RWMutex
的功能,因为为了避免锁定瓶颈,此并发映射被划分为多个(
SHARD\u COUNT
)映射碎片。

这比只使用一个
RWMutex

要快得多,有很多方法可以从
映射
中清除内容,而无需快速访问映射。应用程序的工作方式在很大程度上取决于它的工作方式

0)只需在处理地图时锁定它。如果地图不太大,或者你有一些延迟容忍度,它会很快完成工作(就你花在上面的时间而言),你可以继续考虑其他事情。如果以后出现问题,你可以回到问题上来

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

2) 如果有效的读取基本上是最重要的,那么就使用
atomic.Value
。这使您可以用新的和不同的地图完全替换一个地图。如果写操作基本上占工作负载的0%,那么高效的读操作可以平衡每次更改时创建新映射的成本。这种情况很少见,但也会发生,例如,
encoding/gob
有一个这样管理的全局类型映射

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

还有一个发布和使用之间的竞争问题:goroutine a从地图上获取一些东西,B清除地图并发布东西,a使用发布的东西

一种策略是在使用或释放每个值时锁定它;然后,您需要锁,但不需要全局锁

另一个是容忍种族的后果,如果他们是已知的,而不是灾难性的;例如,其文档明确允许并发访问
net.Conn
s,因此关闭正在使用的连接可能会导致请求出错,但不会导致未定义的应用程序行为。不过,你必须真正确定你知道自己在做什么,因为

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


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

此答案中给出的方法是否足够?(特别是在1.6更改之后?)在语言规范中是否有明确的地方?在上面的代码示例中,映射是从单个goroutine访问的,因此不需要同步,因为没有并发访问。当然,OP假设在迭代过程中会有并发编写器。他说:“我不想阅读锁定地图,因为这会使在迭代过程中删除/修改键变得不可能。”仅仅因为他没有/显示/作者并不意味着他们没有参与。我不太确定问题是什么。如果您询问在不同步的情况下从多个goroutine读取/写入map是否安全,则答案是否定的。但是,有时可以重新写入代码以使访问成为单线程。这就是我试图通过将
delete(m,k)
移出一个单独的goroutine来在我的答案中显示的内容。很难说这个答案是否符合OP的要求,因为问题中没有足够的要求。我们需要更多的上下文来处理这个问题。地图上有什么?它很大吗?发布一个东西有多快?(什么是释放?)如果一个释放的对象被另一个goroutine使用,这是一场灾难吗?如果是,是什么决定了从地图中选择的对象在使用多长时间?应用程序是什么,其优先级是什么(例如,最大化吞吐量与限制延迟)?