std :: map :: end是否是线程安全的并且保证它对于同一个容器始终是相同的?

时间:2016-07-05 23:28:30

标签: c++ multithreading c++11 concurrency c++14

我使用std::map并获得一个我可以使用的元素:http://www.cplusplus.com/reference/map/map/

另外: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这样的内容吗?

http://ideone.com/tATn0H

#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版本修改容器)。没有包含的元素   通过调用访问,但返回的迭代器可用于访问   或修改元素。同时访问或修改不同的   元素是安全的。

2 个答案:

答案 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()仍然是有效的迭代器。