const是否意味着C ++ 11中的线程安全?

我听说constC ++ 11中线程安全的 。 真的吗?

这是否意味着const现在相当于Javasynchronized

他们用完了关键字吗?

我听说constC ++ 11中线程安全的 。 真的吗?

这是真的…

这就是标准语言在线程安全方面所要说的:

[1.10 / 4]如果其中一个修改一个内存位置(1.7)而另一个访问或修改相同的内存位置,则两个expression式评估冲突

[1.10 / 21]一个程序的执行包含一个数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至less有一个不是primefaces的,也不会发生在另一个之前。 任何这样的数据竞争都会导致未定义的行为。

这不是数据竞赛发生的充分条件:

  1. 在一件事物上同时进行两个或两个以上的行动; 和
  2. 至less有一个是写。

标准库build立在这一点上,进一步:

[17.6.5.9/1]本节规定了为防止数据竞争而需要满足的要求(1.10)。 除非另有规定,否则每个标准库函数都应符合每个要求 在下面指定的情况下,实现可能会阻止数据竞争。

[17.6.5.9/3] C ++标准库函数不能直接或间接修改当前线程以外的线程可访问的对象(1.10),除非直接或间接通过函数的非常量参数(包括this访问对象。

简单地说,它期望对const对象的操作是线程安全的 。 这意味着只要对自己types的const对象进行操作, 标准库也不会引入数据竞争

  1. 完全由读取 – 也就是说,没有写入 – 要么
  2. 内部同步写入。

如果这种期望不适用于您的某个types,则直接或间接地与标准库的任何组件一起使用可能会导致数据竞争 。 总之,从标准库的angular度来看, const意思是线程安全的。 需要注意的是,这只是一个契约 ,不会被编译器强制执行,如果你破坏它,你会得到一个未定义的行为,而你是自己的。 const是否存在不会影响代码的生成,至less不会影响数据竞争

这是否意味着const现在相当于Javasynchronized

没有 。 一点也不…

考虑以下代表矩形的过于简化的类:

 class rect { int width = 0, height = 0; public: /*...*/ void set_size( int new_width, int new_height ) { width = new_width; height = new_height; } int area() const { return width * height; } }; 

成员函数 area线程安全的 ; 不是因为它的const ,而是因为它完全由读操作组成。 不涉及写操作,至less需要一次写操作才能使数据竞争发生。 这意味着您可以从任意多个线程中调用area ,并且您将始终获得正确的结果。

请注意,这并不意味着rect线程安全的 。 事实上,很容易看到,如果一个area的调用是在一个给定set_size调用的set_size ,那么该area最终可能会根据旧的宽度和新的高度来计算其结果在乱码上)。

但是,这是rectrect不是const所以它甚至不希望是线程安全的 。 另一方面,声明const rect的对象将是线程安全的,因为不可能写入(如果你正在考虑const_cast最初声明为const东西,那么你会得到未定义的行为 ,就是这样)。

那么这是什么意思?

让我们假设 – 为了争论 – 倍增操作是非常昂贵的,我们最好尽可能避免它们。 我们可以只在需要的时候计算这个区域,然后caching它,以防将来再次请求:

 class rect { int width = 0, height = 0; mutable int cached_area = 0; mutable bool cached_area_valid = true; public: /*...*/ void set_size( int new_width, int new_height ) { cached_area_valid = ( width == new_width && height == new_height ); width = new_width; height = new_height; } int area() const { if( !cached_area_valid ) { cached_area = width; cached_area *= height; cached_area_valid = true; } return cached_area; } }; 

[如果这个例子看起来太过人造,你可以用一个非常大的dynamic分配的整数来replaceint ,这个整数本质上是非线程安全的 ,而且乘法代价极高。]

成员函数 area不再是线程安全的 ,它现在正在执行写操作,并且不在内部进行同步。 这是个问题吗? 对area的调用可能作为另一个对象的复制构造函数的一部分发生,这样的构造函数可能已经被标准容器上的一些操作调用,并且此时标准库期望这个操作作为关于数据读取 比赛 。 但是我们正在写作!

只要我们在标准容器中 rect或间接地插入一个标准容器,我们正在与标准库 签订合同 。 要继续在const函数中进行写操作,同时遵守该合同,我们需要在内部同步这些写操作:

 class rect { int width = 0, height = 0; mutable std::mutex cache_mutex; mutable int cached_area = 0; mutable bool cached_area_valid = true; public: /*...*/ void set_size( int new_width, int new_height ) { if( new_width != width || new_height != height ) { std::lock_guard< std::mutex > guard( cache_mutex ); cached_area_valid = false; } width = new_width; height = new_height; } int area() const { std::lock_guard< std::mutex > guard( cache_mutex ); if( !cached_area_valid ) { cached_area = width; cached_area *= height; cached_area_valid = true; } return cached_area; } }; 

请注意,我们使area函数是线程安全的 ,但是rect仍然不是线程安全的 。 在调用set_size area调用可能仍然计算错误的值,因为widthheight的分配不受互斥锁的保护。

如果我们真的想要一个线程安全 rect ,我们将使用同步原语来保护非线程安全 rect

他们用完了关键字吗?

对,他们是。 自从第一天起,它们就已经用完了关键字


来源 : 你不知道constmutable香草萨特