c ++中的默认复制构造函数是否是线程安全的?

时间:2014-07-31 09:25:52

标签: c++ multithreading copy-constructor

class CSample{
     int a;
     // ..... lots of fields
}
Csample c;

众所周知,Csample有一个默认的复制构造函数。当我这样做时:

Csample d = c

将发生默认的复制构造函数。我的问题是:它是线程安全的吗?因为在执行复制构造函数时,可能有人在另一个线程中修改c。如果是这样,编译器如何做到这一点?如果没有,我认为编译器无法保证复制构造函数是线程安全的,这很可怕。

3 个答案:

答案 0 :(得分:13)

C ++中没有是线程安全的¹,除非明确指出

如果您需要在另一个线程中修改对象c,则需要对其进行锁定。这是一般规则,没有理由为了创建副本而阅读它应该是一个例外。

请注意,正在创建的副本不需要被锁定,因为还没有其他线程知道它。只需要来源。

编译器不保证任何内容都是线程安全的,因为99。%的事情不需要是线程安全的。大多数事情只需要是可重入的。因此,在极少数情况下,您实际上需要创建一些线程安全的东西,您必须使用锁(std::mutex)或原子类型(std::atomic<int>)。

你也可以简单地让你的对象保持不变,然后你就可以在没有锁定的情况下阅读它们,因为创建后没有任何东西在写它们。使用常量对象的代码更容易并行化,并且通常更容易理解,因为您必须跟踪的状态更少。

请注意,在最常见的体系结构中,带有mov操作数int指令恰好是线程安全的。在其他CPU类型甚至可能不是真的。并且因为允许编译器预加载值,所以C ++中的整数赋值仍然是而不是


¹如果在同时对象上同时调用它们,那么一组操作被认为是线程安全的。在C ++中,在同一对象上同时调用任何修改操作和任何其他操作是数据竞争,即UndefinedBehaviour™。

²重要的是要注意,如果一个对象是“线程安全的”,那么无论如何它对你来说并没有多大帮助。因为如果一个对象保证在它同时写入时你将始终读取新值或旧值(当int c0更改为1000时,C ++允许一个线程,另一个线程可能会读取,比如232),大部分时间都无法帮助你,因为你需要在一致状态下读取多个值,无论如何你必须自己锁定它们。 /子>

³可重入意味着可以同时在不同的对象上调用相同的操作。标准C库中有一些可重入的函数,因为它们使用全局(静态)缓冲区或其他状态。大多数都有可重入的变体(通常带有_r后缀),而standrd C ++库使用这些变体,因此C ++部分通常是可重入的。

答案 1 :(得分:5)

标准中的一般规则很简单:如果是一个对象(和 子对象是对象)由多个线程访问, 由任何线程修改,然后所有访问必须是 同步。这有很多原因,但最多 基本的一点就是最低级别的保护通常是 错误的粒度级别;添加同步原语 只会使代码运行速度明显变慢,没有任何代码 用户的真正优势,即使在多线程中也是如此 环境。即使复制构造函数是&#34;线程安全的&#34;, 除非对象在某种程度上完全独立于其他所有对象 在上下文中,您可能需要某种同步 更高层次的原始人。

关于&#34;线程安全&#34;:通常的含义 经验丰富的实践者,它的对象/类/无论如何 确切地说明它保证了多少保护。恰恰 因为像你这样的低等级定义(以及许多,很多 其他)似乎使用是没用的。同步每个功能 一堂课通常没用。 (Java做了实验,并且 然后退缩,因为他们在最初做出的保证 他们的容器版本证明是昂贵的 毫无价值的。)

答案 2 :(得分:1)

假设在多个线程上同时访问dc,这不是线程安全的。这相当于数据争用,这是一种未定义的行为。

Csample d = c;

一样不安全
int d = c;