fra*_*lin 7 c++ multithreading copy-constructor
class CSample{
int a;
// ..... lots of fields
}
Csample c;
Run Code Online (Sandbox Code Playgroud)
我们知道,Csample有一个默认的复制构造函数.当我这样做:
Csample d = c
Run Code Online (Sandbox Code Playgroud)
将发生默认的复制构造函数.我的问题是:它是线程安全的吗?因为c在执行复制构造函数时,可能有人在另一个线程中进行了修改.如果是这样,编译器如何做到这一点?如果没有,我认为编译器无法保证复制构造函数是线程安全的,这太可怕了.
Jan*_*dec 13
除非明确指出,否则 C++中的任何内容都不是线程安全的.
如果您需要读取对象,c而在另一个线程中可能会对其进行修改,则您有责任将其锁定.这是一般规则,没有理由为了创建副本而阅读它应该是一个例外.
请注意,正在创建的副本不需要被锁定,因为还没有其他线程知道它.只需要来源.
编译器不保证任何东西都是自己的线程安全的,因为99.9%的东西不需要是线程安全的.大多数事情只需要是可重入的.因此,在极少数情况下,您实际上需要创建一些线程安全的东西,您必须使用locks(std::mutex)或atomic types(std::atomic<int>).
你也可以简单地让你的对象保持不变,然后你就可以在没有锁定的情况下读取它们,因为在创建之后没有任 使用常量对象的代码更容易并行化,并且通常更容易理解,因为您必须跟踪的状态更少.
请注意,在最常见的体系结构中,mov带有int操作数的指令恰好是线程安全的.在其他CPU类型甚至可能不是真的.并且因为允许编译器预加载值,所以C++中的整数赋值无论如何都不是.
¹ 如果在同一个对象上同时调用它们,则一组操作被认为是线程安全的.在C++中,在同一对象上同时调用任何修改操作和任何其他操作是数据竞争,即UndefinedBehaviour™.
² 重要的是要注意,如果一个对象是"线程安全的",它在大多数情况下并不能真正帮助你.因为如果一个对象保证当它同时写入时你将始终读取新值或旧值(C++允许在一个线程int c被更改0为1000一个线程时,另一个线程可能会读取,比如说232),大多数情况下不会帮助你,因为你需要以一致的状态读取多个值,无论如何你必须自己锁定它们.
³ 可重入意味着可以同时在不同对象上调用相同的操作.标准C库中有一些函数不可重入,因为它们使用全局(静态)缓冲区或其他状态.大多数都有可重入的变体(_r通常带后缀),而standrd C++库使用这些,因此C++部分通常是可重入的.
标准中的一般规则很简单:如果一个对象(和子对象是对象)被多个线程访问, 并且被任何线程修改,则必须同步所有访问.造成这种情况的原因有很多,但最基本的原因是最低级别的保护通常是错误的粒度级别 ; 添加同步原语只会使代码运行速度明显变慢,对用户没有任何实际优势,即使在多线程环境中也是如此.即使复制构造函数是"线程安全的",除非对象在某种程度上完全独立于所有其他上下文,否则您可能需要某种更高级别的同步原语.
关于"线程安全":经验丰富的实践者通常意义上的对象/类/任何事物确切地指出它保证了多少保护.正是因为像你(以及许多其他人)这样的低级定义似乎没用.同步类中的每个函数通常是无用的.(Java进行了实验,然后退出了,因为他们在容器的初始版本中所做的保证变得昂贵且毫无价值.)
假设在多个线程上同时访问d或c,则这不是线程安全的。这将构成数据竞争,这是未定义的行为。
Csample d = c;
Run Code Online (Sandbox Code Playgroud)
和一样不安全
int d = c;
Run Code Online (Sandbox Code Playgroud)
是。
| 归档时间: |
|
| 查看次数: |
1297 次 |
| 最近记录: |