如果不同的线程始终使用不同的键,它们能否向映射表中插入数据?

3

我正在尝试为一个对象设计消息队列。有一组X个线程,它们都可以向该对象发送消息(稍后处理)。如果我有一个std :: map<thread_id_t,message>,假设线程1仅添加具有键1的消息,线程2添加具有键2的消息等等,那么这是线程安全的吗?


1
不,它不是线程安全的。 - Andy Prowl
不,你需要同步访问。这个地图是一个共享资源,来自不同线程的每个调用都会改变它的状态。 - Marius Bancila
你有标准文件的链接吗?这个回答:https://dev59.com/HGUp5IYBdhLWcg3wxpmt#15067564提到“当在同一序列中的不同元素中包含对象的内容时,实现必须避免数据竞争,除了vector<bool>”--这不适用于这里吗? - Sam Kellett
@SamKellett:这是不同的。你在此并发修改的是数据结构本身(通过插入新的键值对),而不是其中包含的单个元素 :) - Andy Prowl
2个回答

5

std::map不支持多个同时写入。这是STL maps不支持线程安全的许多原因中的一种。STL map的底层实现是AVL树,在插入一定数量的元素后需要重新平衡。重新平衡会影响多个节点,绝对不是线程安全的。

如果您对此感兴趣,请参考优秀的关于无锁数据结构的Dr. Dobb文章


啊,好的,那么像这样:std::vector<std::vector<message>>,其中每个线程 ID 都被映射到外部向量中的一个索引,会是一个更好的想法吗? - Sam Kellett
@DietmarKühl:我的说法是“对于多个同时写入者来说不是线程安全的”。我认为这相当准确,对吗? - EyasSH
1
可以同时修改不同的向量,没错。但是需要确保这些向量按照顺序和线性方式插入到外部向量中。如果“在线程创建时”指的是在父线程中,则可能是可以的。 - EyasSH
1
实际上,即使如此也可能不可取,除非在任何线程开始运行之前创建了所有线程,因为任何插入都可能移动包含在外部向量中的向量。如果您知道您拥有的线程最大数量,只需拥有一个向量数组或指向向量的指针数组- 不可调整大小,并且可以在并行中完美访问。 - EyasSH
1
我知道有多少个线程,但在运行时而不是编译时需要使用向量。但可以保证,在任何内部向量被使用之前,外部向量已经被填充并“冻结”。 - Sam Kellett
显示剩余4条评论

1
标准C++库中类的一般规则是:如果你在一个对象上调用了非const方法(除了一些方法,如std::vector<T>::operator[]()),则不能让任何其他线程以任何方式同时访问此对象。如果需要使用这些操作,则需要在不同线程之间同步访问。标准中相关的条款是17.6.4.10 [res.on.objects]第1段:

如果来自不同线程的对标准库函数的调用可能引入数据竞争,则程序的行为未定义。可能出现这种情况的条件在17.6.5.9中指定。

...和17.6.5.9 [res.on.data.races]描述了标准C++库不允许在没有调用非const成员函数的情况下改变对象。

由于将对象插入std::map<...>显然是非const操作,因此在没有同步的情况下不能同时从多个线程进行插入操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接