问题描述
我有一个地图,需要在清除地图之前释放对象。
I have a map with objects that needs to be released before clearing the map. I am tempted to iterate over the map and remove/release objects as I walk through it.
这里是一个模拟示例
因为迭代地图的唯一方法是通过范围,我如何同步多个生产者和多个消费者?
Since the only way to iterate the map is through range, how would I synchronize multiple producers and multiple consumers?
我不想读锁定地图,因为
I don't want to read lock the map since that would make delete/modifying keys during the iteration impossible.
推荐答案
有很多方法可以清除一个 map
没有racy地图访问。什么适用于你的应用程序取决于它在做什么。
There are a bunch of ways you can clean up things from a map
without racy map accesses. What works for your application depends a lot on what it's doing.
0)只要在工作时锁定地图即可。如果地图不太大,或者你有一些延迟容忍,它会迅速完成工作(根据你花费的时间),你可以继续考虑其他的东西。
0) Just lock the map while you work on it. If the map's not too big, or you have some latency tolerance, it gets the job done quickly (in terms of time you spend on it) and you can move on to thinking about other stuff. If it becomes a problem later, you can come back to the problem then.
1)将对象或指针复制出来并在持有锁的同时清除地图,然后释放在背景中的对象。如果问题是缓慢的释放本身将保持锁持有很长时间,这是一个简单的解决方法。
1) Copy the objects or pointers out and clear the map while holding a lock, then release the objects in the background. If the problem is that the slowness of releasing itself will keep the lock held a long time, this is the simple workaround for that.
2)如果高效的读取基本上都是重要的是,使用 atomic.Value
。这样,您就可以使用新的和不同的地图完全替换一个地图。如果写入本质上是您工作负载的0%,则高效读取将平衡每次更改时创建新映射的成本。这是罕见的,但它发生,例如, encoding / gob
有这样管理的类型的全局地图。
2) If efficient reads are basically all that matters, use atomic.Value
. That lets you entirely replace one map with a new and different one. If writes are essentially 0% of your workload, the efficient reads balance out the cost of creating a new map on every change. That's rare, but it happens, e.g., encoding/gob
has a global map of types managed this way.
3)如果没有一个做你需要的一切,调整如何存储数据(例如分片映射)。用自己的16个地图和哈希键来替换你的地图,以决定一个物体属于哪个地图,然后你可以一次锁定一个碎片,以进行清理或任何其他写入。
3) If none of those do everything you need, tweak how you store the data (e.g. shard the map). Replace your map with 16 maps and hash keys yourself to decide which map a thing belongs in, and then you can lock one shard at a time, for cleanup or any other write.
还有一个发布和使用之间的竞争的问题:goroutine A从地图获取东西,B清除地图和释放的东西,A使用释放的东西。
There's also the issue of a race between release and use: goroutine A gets something from the map, B clears the map and releases the thing, A uses the released thing.
一个策略是在使用或释放时锁定每个值;那么你需要锁而不是全局锁。
One strategy there is to lock each value while you use or release it; then you need locks but not global ones.
另一个是容忍比赛的后果,如果他们是已知的,不是灾难性的;例如,并发访问 net.Conn
s是由其文档显式允许的,因此关闭一个使用中的连接可能会导致一个请求它的错误,但不会导致到未定义的应用程序行为。你必须确定你知道你是什么,然后,因为,因为许多良性的
Another is to tolerate the consequences of races if they're known and not disastrous; for example, concurrent access to net.Conn
s is explicitly allowed by its docs, so closing an in-use connection may cause a request on it to error but won't lead to undefined app behavior. You have to really be sure you know what you're getting into then, though, 'cause many benign-seeming races aren't.
最后,也许你的应用程序已经确保没有释放正在使用的对象,例如对象上有一个安全维护的引用计数,只释放未使用的对象。然后,当然,你不必担心。
Finally, maybe your application already is ensuring no in-use objects are released, e.g. there's a safely maintained reference count on objects and only unused objects are released. Then, of course, you don't have to worry.
这可能是诱人的尝试用渠道替换这些锁,不知何故,但我没有看到任何收益它。这是很好,当你可以设计你的应用程序思维主要是在进程之间的通信,而不是共享数据,但当你有共享数据,没有用假装否则。排除对共享数据的不安全访问是锁定。
It may be tempting to try to replace these locks with channels somehow but I don't see any gains from it. It's nice when you can design your app thinking mainly in terms of communication between processes rather than shared data, but when you do have shared data, there's no use in pretending otherwise. Excluding unsafe access to shared data is what locks are for.
这篇关于Golang并发地图访问与范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!