如何在添加和删除元素时转储无序地图?
Posted
技术标签:
【中文标题】如何在添加和删除元素时转储无序地图?【英文标题】:How to dump unordered map while adding and erasing elements to it? 【发布时间】:2013-02-03 14:31:02 【问题描述】:我想转储 unordered_map 的键,同时能够同时添加和删除元素。完全转储需要 4 秒,太长了。是否可以在单独的线程中转储,如下所示:
while (1)
pthread_mutex_lock( &mutex );
if(iter!=map.end())
x=iter->first
iter++;
pthread_mutex_unlock( &mutex );
do_this(x); // this takes time to complete
在主线程中我有:
pthread_mutex_lock( &mutex );
map.erase(iter);
unordered map的erase方法是否会出问题,因为iterator在擦除后会失效。
还有其他安全的并行转储方式吗?
【问题讨论】:
“do_this”到底是做什么的? @Mats:没关系。 我在寻找“是否有其他方法可以解决‘需要 4 秒’”,而不是直接回答这个问题。do_this
执行 fstream 操作。所以我不能让它更快。
【参考方案1】:
对于unordered_map
(以及一般的关联容器),erase()
成员函数不会使迭代器和对除已删除元素之外的其他元素的引用无效。
但是,在这里,您可能会擦除一个元素并使其迭代器无效,而您的循环持有该元素的迭代器:例如,如果您碰巧擦除了将要被取消引用的下一个迭代器所引用的元素你的循环。
因此,您需要注意要删除的元素不被您将在while
循环的下一个循环中处理的迭代器引用:
pthread_mutex_lock( &mutex );
if (i != iter)
map.erase(i);
else
// Maybe store in a queue of elements to be removed after the loop is done
其中iter
是循环中使用的迭代器变量。
【讨论】:
如果在未同步的 do_this() 调用期间 iter 被其他线程擦除怎么办 - 它将失效,并且下一个增量将导致 UB。 @jmetcalfe:我从问题的文本中假设do_this()
处理元素但不修改地图。现在我有点困惑,至于 where do_this()
应该是
我认为 do_this() 并不重要,只是您持有迭代器而不持有互斥锁这一事实,因此其他线程可以出现并删除该迭代器。
@jmetcalfe:嗯,do_this()
不是持有迭代器,而是持有一个元素。我的假设是 do_this()
不适用于地图,因此它不会保存迭代器。
围绕 do_this() 的循环仍然持有迭代器,并在下一次迭代中递增它【参考方案2】:
见:What happens if you call erase() on a map element while iterating from begin to end?
由于您在调用 do_this
方法(调用 erase
)之前递增迭代器,因此不会造成任何麻烦。
只是一个想法:使用您当前的算法,我认为您根本不需要互斥锁。
【讨论】:
我的理解是,它是一个单独的线程调用擦除,而不是 do_this 调用,它做转储?【参考方案3】:您可以通过迭代桶而不是迭代元素来获得一些(但不是全部 - 这实际上只是允许您交错操作,以便擦除不必等待整个 4 秒)所需的并行性。只要不减少桶数,这将是安全的
即
pthread_mutex_lock( &mutex );
size_t count = map.bucket_count();
pthread_mutex_unlock( &mutex );
for(size_t i = 0; i<count; ++i)
pthread_mutex_lock( &mutex );
for(auto it = map.begin(i); it != map.end(i); ++i)
do_this(it->first);
pthread_mutex_unlock( &mutex );
如果您想将 do_this 从互斥锁中提取出来,您需要在其他结构中累积值
另一个建议,具体取决于此地图在其他地方的使用方式,您可以将元素交换为某个已知的无效值而不是擦除,然后让执行转储的线程/ do_this 在看到这个值时执行实际擦除。
【讨论】:
以上是关于如何在添加和删除元素时转储无序地图?的主要内容,如果未能解决你的问题,请参考以下文章