由并发无序映射查找线程调用的函数是不是安全?

Posted

技术标签:

【中文标题】由并发无序映射查找线程调用的函数是不是安全?【英文标题】:Are functions called by a concurrent unordered map look-up thread safe?由并发无序映射查找线程调用的函数是否安全? 【发布时间】:2018-09-13 16:00:51 【问题描述】:

假设我有以下代码:

#include<concurrent_unordered_map.h>

struct firstStruct 
<irrelevant code>

struct secondStruct 
    void func() 
       <CRITICAL CODE>
    

假设我有这个:

concurrent_unordered_map<firstStruct,secondStruct> cmap = ...

让我们假设cmap 已被填充,并且firstStruct fsecondStruct s(key,value)(f,s) 存在于cmap 中。

如果我使用下面的代码 sn-p

cmap[f].func();

secondStruct s 执行func() 中的&lt;CRITICAL CODE&gt; 是否是线程安全的?

另外,如果我有以下代码 sn-ps,可能会在不同线程中同时执行,会发生什么?

cmap[f].func();

SecondStruct s2 = ... ;
cmap[f] = s2;

如果(key,value)(f,s)被一个线程更改为(f,s2),如果另一个线程恰好正在执行&lt;CRITICAL CODE&gt;会发生什么?

【问题讨论】:

【参考方案1】:

从proposal 到concurrent_unordered_map

对于序列化执行,操作的行为与其当前的 STL 对应项相同。唯一的变化是允许并发。在并发无序容器上同时执行以下任何操作不会引入数据竞争:

get_allocator
empty, size, max_size
begin, end, cbegin, cend
insert
find, count, equal_range, operator[], at
load_factor
max_load_factor() 
operator==, operator!= 

假设对键类型(和 mapped_type,如果适用)的必要操作是并发安全的。

强调我的

所以,operartor[] 是线程安全的,但您使用它的返回值执行的操作也必须是线程安全的,以保证不会发生数据竞争。这意味着func() 中的&lt;CRITICAL CODE&gt; 本身必须是线程安全的。如果不是,那么类似

cmap[f].func();
SecondStruct s2 = ... ;
cmap[f] = s2;

也不会是线程安全的,因为当您将新对象分配给键时,该函数仍会运行。

【讨论】:

非常感谢!这消除了我在代码中几个地方的一些疑问。为了解决这个问题,我考虑将cmap 更改为:concurrent_unordered_map&lt;firstStruct,std::shared_ptr&lt;secondStruct&gt;&gt; cmap = ... 并像这样调用 func:std::shared_ptr&lt;secondStruct&gt; temp_ptr = cmap[f]; temp_ptr-&gt;func(); 这会是线程安全的吗?通常有没有更好的方法来做到这一点,同时保持线程安全并确保快速执行?在这一点上我应该指出,速度在我的代码中很重要。 @glopquestions 只要没有人做类似*cmap[f] = s2; 的事情(分配给指向的对象而不是分配一个新指针),那将是线程安全的。如果他们分配了一个新的指针,你会没事的,但如果他们改变了你身上的底层对象,那么你就在同一条船上。 我在考虑使用cmap[f] = std::make_shared&lt;secondStruct&gt;(s2); 分配这是线程安全的吗? (只是为了澄清,将 f 映射到同一个共享指针但更改共享指针指向的内容(我想要的不同的东西)是危险的),但是(将 f 映射到新的共享指针并拥有这个新的共享指针指向我想要的任何东西都是安全的?) @glopquestions 是的,那将是线程安全的。 cmap[f] 将被重新分配给 std::make_shared&lt;secondStruct&gt;(s2),但由于 temp_ptr 存在,那么您调用该函数的对象仍然存在。 @glopquestions 线程安全是一个相对属性。询问单个操作是否是线程安全的属于类别错误。询问两个或多个操作是否相对线程安全是有意义的。您甚至可以询问同一操作的两次调用是否对彼此是线程安全的。这些 cmets 中的代码有一些线程安全的操作;这些 cmets 中的代码还有其他不是线程安全的操作。如果你问你的做法,对方必须读懂你的想法,如果他们弄错了,他们会给出错误的答案,而你会幸福地浑然不觉。

以上是关于由并发无序映射查找线程调用的函数是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章

线程安全与可重入函数

多线程并发为什么不安全

在无序映射中插入智能指针调用析构函数

如何设计并实现一个线程安全的 Map

多个线程调用同一个函数是不是安全?

从 C++ 中的多个线程调用 Qt 中小部件类的信号函数是不是安全?