2014-09-23 42 views
4

我可以访问(不锁定)一个std :: map条目,而另一个线程插入/擦除entrys?从另一个线程插入/擦除时,是否可以访问C++ 11 std :: map条目?

例如伪C++:

typedef struct { 
    int value; 
    int stuff; 
}some_type_t; 

std::map<char,some_type_t*> my_map; 

//thread 1 does: 
my_map.at('a')->value = 1; 

//thread 2 does: 
some_type_t* stuff = my_map.at('b'); 

//thread 3 does: 
my_map.erase('c'); 

//I'm not modifying any elements T is a pointer to an previously allocated "some_type_t" 

STD C++ 11说,所有成员都应该是线程安全的(删除()不是常量)。

回答

9

无无无无无!

map::erase使用map::at中使用的搜索算法修改映射条目和混淆之间的链接。您可以在擦除期间使用这些元素,但不能执行搜索算法本身!

我创建了一个图形程序。在我的机器上,这个程序有时打印OK,有时会抛出一个地图异常 - 这意味着搜索出错了。增加阅读线程的数量会使异常更频繁地出现。

#include <iostream> 
#include <thread> 
#include <map> 
#include <cassert> 

std::map<int, int> m; 

// this thread uses map::at to access elements 
void foo() 
{ 
    for (int i = 0; i < 1000000; ++i) { 
    int lindex = 10000 + i % 1000; 
    int l = m.at(lindex); 
    assert(l == lindex); 
    int hindex = 90000 + i % 1000; 
    int h = m.at(hindex); 
    assert(h == hindex); 
    } 
    std::cout << "OK" << std::endl; 
} 

// this thread uses map::erase to delete elements 
void bar() 
{ 
    for (int i = 20000; i < 80000; ++i) { 
    m.erase(i); 
    } 
} 

int main() 
{ 
    for (int i = 0; i < 100000; ++i) { 
    m[i] = i; 
    } 

    std::thread read1(foo); 
    std::thread read2(foo); 
    std::thread erase(bar); 

    read1.join(); 
    read2.join(); 
    erase.join(); 

    return 0; 
} 
+0

+1表示答案是'否' – Soren 2014-09-25 06:47:32

-1

只要线程不同时访问相同的地址不会有问题。

但对于直接回答你的问题,不,它不是,所以你不能没有没有任何错误锁定去做线程安全的。

+1

您需要更清楚地明白“访问同一地址”的含义 - 删除或插入一个条目会影响地图中链接的其他元素。 – Soren 2014-09-25 06:51:23

11

号是的。

两个或多个线程可在地图,其中一些非const操作也算执行const操作(即返回非const迭代运算,因此不是const,像begin和类似的东西)。

您还可以修改不读/写/删除上述元素的非关键部件的运行(这是大多数人的mapset而其它操作元素的非关键部件,除了像erase东西或=)。

你不能eraseinsert或其它类似的非const地图操作,同时与const和类似的地图操作(如findat)做任何事情。请注意,如果添加元素,则[]可能类似于erase

该标准有非const操作即算作const线程安全的目的明确的名单 - 但他们基本上返回iterator&查找。

+0

非常好的解释。 – Surt 2014-09-23 13:05:55

0

号的std ::地图是不是线程安全的。英特尔的线程构建块(tbb)库有一些并发容器。检查tbb

+0

这取决于您的线程安全定义,这不是一个明确定义的术语。例如,'std :: map'符合C++标准的线程安全要求,所以是“线程安全的”,但是这些要求不允许并发读/写单个对象,所以它也不是“线程安全的”安全”。除非你明确说明你是如何使用它,否则最好避免使用这个术语。 – 2016-01-28 13:41:36

相关问题