我使用std::map
并获得一个我可以使用的元素:http://www.cplusplus.com/reference/map/map/
iterator find (const key_type& k);
mapped_type& at (const key_type& k);
mapped_type& operator[] (const key_type& k);
另外:lower_bound()
或equal_range()
- 在这种情况下与find()
相同。
我无法使用:
at()
- 因为它抛出异常,我测量了性能下降10倍operator[]
- 因为它插入一个元素(如果它不存在),这种行为是不可接受的 find()
- 就是我想要的。但我在多线程程序中使用std::map
并通过锁std::mutex
保护它。
还有std::map
从其他线程插入和删除。
我应该保护std::map::end
还是保证一个分配的容器始终相同?
我可以使用不受static auto const map_it_end = map1.end();
std::mutex
这样的内容吗?
#include <iostream>
#include <string>
#include <mutex>
#include <thread>
#include <map>
std::map<std::string, std::string> map1 ( {{"apple","red"},{"lemon","yellow"}} );
static auto const map_it_end = map1.end();
std::mutex mtx1;
void func() {
std::lock_guard<std::mutex> lock1(mtx1);
auto it1 = map1.find("apple");
if(it1 != map_it_end) // instead of: if(it1 != map1.end())
std::cout << it1->second << ", ";
}
int main ()
{
std::thread t1(func);
std::thread t2(func);
t1.join();
t2.join();
return 0;
}
http://www.cplusplus.com/reference/map/map/end/
数据竞赛访问容器(既不是const也不是 非const版本修改容器)。没有包含的元素 通过调用访问,但返回的迭代器可用于访问 或修改元素。同时访问或修改不同的 元素是安全的。
答案 0 :(得分:11)
我应该保护
std::map::end
还是保证一个分配的容器始终相同?
从技术上讲,任何对成员函数的调用都必须受到互斥锁的保护,如果它可能与任何非const成员函数同时发生。因此,如果任何线程可以插入或删除元素,那么在不锁定互斥锁的情况下调用end()
是不安全的。
我可以使用不受
保护的static auto const map_it_end = map1.end();
std::mutex
这样的内容吗?
在某些情况下,您可以缓存过去的迭代器,因为std::map
的过去的迭代器不会因插入和删除而失效,只能通过交换或移动地图来实现。 / p>
但你为什么要这样做?缓慢的操作是find()
而不是end()
,所以如果您在仍然持有互斥锁的情况下调用end()
,那么肯定会有效。
如果其他线程可能正在擦除元素,那么您需要在取消引用find()
返回的迭代器时保持互斥锁,以确保它不会被另一个删除它引用的元素的线程无效。因此,当您已经锁定互斥锁时,拨打end()
并不会成为问题。
答案 1 :(得分:2)
我在23.2 Container requirements
中找不到任何内容,指出end()
总是返回相同的值,也不是线程安全的。 end()
定义如下。
begin()返回一个迭代器,引用该中的第一个元素 容器。 end()返回一个迭代器,它是一个过去的结束值 对于容器。如果容器为空,则begin()== end();
此规范似乎涵盖了所有容器的end()
。我在23.4.4 Class template map
中找不到任何可以取代这种一般容器要求的东西。实际上,“过去的最终价值”的措辞是这样的,因此可以合理地解释为end()
的值可能会根据容器中最后一个元素的位置/位置而改变。
这将是std::vector
的典型案例。由于显而易见的原因,典型的std::vector
end()
值会根据向量中的元素数量而变化。没有任何规定它必须,但通常情况就是如此。回到std::map
,人们可能会认为给定地图的end()
始终是相同的值,但也没有任何说明它必须。
我想说,对std::map
的所有访问都必须受互斥锁的保护。释放互斥锁后,地图的任何内容都不再有效。在互斥体被释放后,不能假设end()
仍然是有效的迭代器。