Ark*_*nez 4 c++ multithreading stl
我在stl向量上有几个编写器(线程)和一个读者.
正常的写入和读取是互斥保护,但我想避免在我有一个循环上的争用,我想知道vector :: size是否足够安全,我想这取决于实现,但因为通常矢量动态内存是为了存储项目在重新分配期间不应使存储大小的内存无效.
我不介意误报,在大小> 0之后,我实际上会锁定并再次检查,所以如果读取size()而另一个线程写入并不是段错误,那么对我来说它应该足够安全.
我不知道并发读取和写入整数段错误的实现(尽管C++ 03标准没有禁止这一点,我不知道POSIX是否这样做).如果向量使用pImpl,并且没有将大小存储在向量对象本身中,那么在尝试从另一个线程中释放的pImpl对象读取大小时可能会出现问题.例如,我的机器上的GCC确实使用了pImpl(并且没有直接存储大小 - 它被计算为begin()和end()之间的差异,因此在修改期间有明显的竞争条件机会).
尽管它不会崩溃,但它很可能会给出一个毫无意义或错误的答案.如果您没有锁定,那么您读取的值可能是:
非原子地读,意味着你获得一个值的最重要的一半和另一个的最不重要的一半.实际上,在大多数实现中读取size_t可能是原子的,因为size_t有很好的理由成为体系结构的自然字大小.但是如果它发生了,当"之前"而不是"之后"都不是0时,这可以将值读为0.考虑例如转换0x00FF - > 0x0100.如果你得到"之后"的下半部分和"之前"的上半部分,你就读到了0.
任意陈旧.如果没有锁定(或其他一些内存屏障),您可以从缓存中获取值.如果该缓存不与其他CPU /核共享,并且您的体系结构没有所谓的"连贯缓存",则运行不同线程的不同CPU或核心可能在六周前改变了大小,并且您永远不会看到新的价值.此外,不同的地址可能是不同的数量陈旧 - 没有内存障碍,如果另一个线程已经完成了push_back,你可以想象"看到"你的向量末尾的新值但不"看到"增加的大小.
很多这些问题都隐藏在常见的架构上.例如,x86具有连贯的缓存,MSVC在访问volatile
对象时保证完全的内存屏障.ARM并不保证一致的缓存,但实际上多核ARM并不常见,因此双重检查锁定通常也适用于此.
这些保证解决了一些困难,并允许一些优化,这就是为什么它们首先制造,但它们不是普遍的.显然,如果不做出超出C++标准的某些假设,就不能编写多线程代码,但是依赖于特定于供应商的保证,代码的可移植性就越低.除了参考特定实现之外,不可能回答您的问题.
如果您正在编写可移植代码,那么您应该将所有内存读取和写入视为可能存在于线程自己的专用内存缓存中.内存障碍(包括锁)是一种"发布"写入和/或"导入"来自其他线程的写入的方法.与版本控制系统(或您最喜欢的其他任何本地副本示例)的类比很清楚,不同之处在于,即使您不要求它们也可以随时发布/导入.当然,没有合并或冲突检测,除非行业最终实现了事务性内存而我不看;-)
在我看来,多线程代码应首先避免共享内存,然后在绝对必要时锁定,然后配置文件,然后担心争用和无锁算法.进入最后阶段后,您需要研究并遵循针对特定编译器和体系结构的经过良好测试的原则和模式.C++ 0x将通过标准化你可以依赖的一些东西来帮助,Herb Sutter的一些"有效并发"系列详细介绍了如何利用这些保证.其中一篇文章有一个无锁多写入器单读取器队列的实现,它可能适合或不适合您的目的.
归档时间: |
|
查看次数: |
515 次 |
最近记录: |