[英]Does const mean thread-safe in C++11?
I hear that const
means thread-safe in C++11 .我听说
const
在C++11 中意味着线程安全。 Is that true?真的吗?
Does that mean const
is now the equivalent of Java 's synchronized
?这是否意味着
const
现在等同于Java的synchronized
?
Are they running out of keywords ?他们的关键字用完了吗?
I hear that
const
means thread-safe in C++11 .我听说
const
在C++11 中意味着线程安全。 Is that true?真的吗?
It is somewhat true...这有点真实...
This is what the Standard Language has to say on thread-safety:这就是标准语言关于线程安全的说法:
[1.10/4] Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one accesses or modifies the same memory location.
[1.10/4]如果其中一个修改了内存位置 (1.7) 而另一个访问或修改了相同的内存位置,则两个表达式评估会发生冲突。
[1.10/21] The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other.
[1.10/21]如果一个程序在不同的线程中包含两个相互冲突的动作,那么它的执行就会包含数据竞争,其中至少一个不是原子的,并且两者都不在另一个之前发生。 Any such data race results in undefined behavior.
任何此类数据竞争都会导致未定义的行为。
which is nothing else than the sufficient condition for a data race to occur:这只不过是发生数据竞争的充分条件:
The Standard Library builds on that, going a bit further:标准库以此为基础,更进一步:
[17.6.5.9/1] This section specifies requirements that implementations shall meet to prevent data races (1.10).
[17.6.5.9/1]本节规定了实现应满足的要求,以防止数据竞争(1.10)。 Every standard library function shall meet each requirement unless otherwise specified.
除非另有说明,否则每个标准库函数都应满足每个要求。 Implementations may prevent data races in cases other than those specified below.
在下面指定的情况以外的情况下,实现可以防止数据竞争。
[17.6.5.9/3] A C++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's non- const arguments, including
this
.[17.6.5.9/3] C++ 标准库函数不应直接或间接修改当前线程以外的线程可访问的对象 (1.10),除非这些对象是通过函数的非const参数直接或间接访问的,包括
this
。
which in simple words says that it expects operations on const
objects to be thread-safe .简而言之,它希望对
const
对象的操作是线程安全的。 This means that the Standard Library won't introduce a data race as long as operations on const
objects of your own types either这意味着只要对您自己类型的
const
对象进行操作,标准库就不会引入数据竞争
If this expectation does not hold for one of your types, then using it directly or indirectly together with any component of the Standard Library may result in a data race .如果这种期望不适用于您的一种类型,那么直接或间接将它与标准库的任何组件一起使用可能会导致数据竞争。 In conclusion,
const
does mean thread-safe from the Standard Library point of view.总之,从标准库的角度来看,
const
确实意味着线程安全。 It is important to note that this is merely a contract and it won't be enforced by the compiler, if you break it you get undefined behavior and you are on your own.重要的是要注意,这只是一个合同,编译器不会强制执行,如果你违反它,你会得到未定义的行为,你是自己的。 Whether
const
is present or not will not affect code generation --at least not in respect to data races --. const
是否存在不会影响代码生成——至少不会影响数据竞争——。
Does that mean
const
is now the equivalent of Java 'ssynchronized
?这是否意味着
const
现在等同于Java的synchronized
?
No .没有。 Not at all...
一点也不...
Consider the following overly simplified class representing a rectangle:考虑以下表示矩形的过于简化的类:
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;
}
};
The member-function area
is thread-safe ;成员函数
area
是线程安全的; not because its const
, but because it consist entirely of read operations.不是因为它的
const
,而是因为它完全由读取操作组成。 There are no writes involved, and at least one write involved is necessary for a data race to occur.不涉及写入,并且至少需要涉及一次写入才能发生数据竞争。 That means that you can call
area
from as many threads as you want and you will get correct results all the time.这意味着您可以根据需要从任意数量的线程中调用
area
,并且您将始终获得正确的结果。
Note that this doesn't mean that rect
is thread-safe .请注意,这并不意味着
rect
是线程安全的。 In fact, its easy to see how if a call to area
were to happen at the same time that a call to set_size
on a given rect
, then area
could end up computing its result based on an old width and a new height (or even on garbled values).事实上,它很容易看到,如果一个呼叫
area
是在同一时间,一个呼叫发生set_size
在给定的rect
,然后area
最终可能会计算基于一个古老的宽度和一个新的高度的结果(甚至关于乱码值)。
But that is alright, rect
isn't const
so its not even expected to be thread-safe after all.但这没关系,
rect
不是const
所以它毕竟不是线程安全的。 An object declared const rect
, on the other hand, would be thread-safe since no writes are possible (and if you are considering const_cast
-ing something originally declared const
then you get undefined-behavior and that's it).另一方面,声明为
const rect
的对象将是线程安全的,因为不可能写入(如果您正在考虑const_cast
-ing 最初声明为const
东西,那么您会得到未定义的行为,仅此而已)。
So what does it mean then?
那么这意味着什么呢?
Let's assume --for the sake of argument-- that multiplication operations are extremely costly and we better avoid them when possible.让我们假设——为了论证——乘法运算的成本非常高,我们最好尽可能避免它们。 We could compute the area only if it is requested, and then cache it in case it is requested again in the future:
我们可以仅在请求时计算该区域,然后将其缓存以备将来再次请求时:
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;
}
};
[If this example seems too artificial, you could mentally replace int
by a very large dynamically allocated integer which is inherently non thread-safe and for which multiplications are extremely costly.] [如果这个例子看起来太人为,你可以用一个非常大的动态分配的整数替换
int
,这个整数本质上是非线程安全的,并且乘法非常昂贵。]
The member-function area
is no longer thread-safe , it is doing writes now and is not internally synchronized.成员函数
area
不再是线程安全的,它现在正在执行写入操作并且内部不同步。 Is it a problem?这是个问题吗? The call to
area
may happen as part of a copy-constructor of another object, such constructor could have been called by some operation on a standard container , and at that point the standard library expects this operation to behave as a read in regard to data races .对
area
的调用可能作为另一个对象的复制构造函数的一部分发生,这样的构造函数可能已被标准容器上的某些操作调用,并且此时标准库希望此操作表现为对数据的读取比赛。 But we are doing writes!但我们正在写!
As soon as we put a rect
in a standard container --directly or indirectly-- we are entering a contract with the Standard Library .一旦我们将
rect
放入标准容器中—— rect
或间接——我们就与标准库签订了合同。 To keep doing writes in a const
function while still honoring that contract, we need to internally synchronize those writes:为了继续在
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;
}
};
Note that we made the area
function thread-safe , but the rect
still isn't thread-safe .请注意,我们创建了
area
函数thread-safe ,但rect
仍然不是thread-safe 。 A call to area
happening at the same time that a call to set_size
may still end up computing the wrong value, since the assignments to width
and height
are not protected by the mutex.对
area
的调用与对set_size
的调用set_size
, set_size
可能仍会计算出错误的值,因为对width
和height
的分配不受互斥锁保护。
If we really wanted a thread-safe rect
, we would use a synchronization primitive to protect the non-thread-safe rect
.如果我们真的想要一个线程安全的
rect
,我们会使用同步原语来保护非线程安全的rect
。
Are they running out of keywords ?
他们的关键字用完了吗?
Yes, they are.对,他们是。 They have been running out of keywords since day one.
从第一天起,他们的关键字就用完了。
Source : You don't know const
and mutable
- Herb Sutter来源: 你不知道
const
和mutable
- Herb Sutter
This is an addition to K-ballo's answer.这是 K-ballo 答案的补充。
The term thread-safe is abused in this context.在这种情况下,术语线程安全被滥用。 The correct wording is: a const function implies
thread-safe
bitwise const or internally synchronised , as stated by Herb Sutter (29:43) himself正确的措辞是:如Herb Sutter (29:43) 自己所述, const 函数意味着
线程安全的
按位 const或内部同步
It should be thread-safe to call a const function from multiple threads simultaneously, without calling a non-const function at the same time in another thread.同时从多个线程调用 const 函数应该是线程安全的,而不是在另一个线程中同时调用非常量函数。
So, a const function should not (and will not most of the time) be really thread-safe, as it may read memory (without internal synchronisation) that could be changed by another non-const function.因此,const 函数不应该(并且在大多数情况下不会)真正是线程安全的,因为它可能会读取可由另一个非常量函数更改的内存(没有内部同步)。 In general, this is not thread-safe as a data race occurs even if only one thread is writing (and another reading the data).
通常,这不是线程安全的,因为即使只有一个线程正在写入(另一个线程正在读取数据)也会发生数据竞争。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.