【问题标题】:simultaneous reading and writing to vector cause exception同时读取和写入向量导致异常
【发布时间】:2011-06-28 21:21:59
【问题描述】:

我有 2 个线程(使用 boost 线程库)和 Windows 上的全局向量(Visual Studio 2005)。我创建了线程 - 首先是读取文件 (vector.push_back(value)) 并增加“读取计数器”,另一个是处理已加载的数据(第二个“指针”记住已处理向量中的索引)。处理类似于vector[i] = function(vector[i]);

当我启动程序时,它以异常结束Unhandled exception at 0x0041350c in program.exe: 0xC0000005: Access violation writing location 0x00b82214. 读取计数器的值小于记录计数(仍在读取文件),vector.size() 为“readCounter + 1”,“处理指针”+/ - 2200(比读取计数器少得多 - 读取空值没有问题)...每次运行的值都不同(有时,不会抛出异常并且程序完成)...

当我删除处理线 (vector[i] = function(vector[i]);) 时没有问题发生......所以我猜,向量不是线程安全的。你能帮我,如何防止这个问题?

【问题讨论】:

  • 使事情线程安全会增加在大多数情况下不需要的大量开销。您应该始终假定标准库不是线程安全的。

标签: c++ windows multithreading boost vector


【解决方案1】:

当前版本的标准 (C++03) 甚至不支持线程,因此 STL 容器是否是线程安全的这一事实完全取决于特定的实现。

在您的情况下,您应该参考 MSDN 的 this page,其中指出

单个对象是线程安全的,可以从多个线程中读取。例如,给定一个对象 A,同时从线程 1 和线程 2 读取 A 是安全的。

如果一个线程正在写入单个对象,则必须保护同一线程或其他线程上对该对象的所有读取和写入。例如,给定一个对象 A,如果线程 1 正在写入 A,则必须阻止线程 2 读取或写入 A。

因此,您必须使用互斥锁或其他同步原语来保护您的读/写。您可以在 Boost.Thread 中找到它们的多平台和 C++ 友好的实现(顺便说一下,这是 C++0x 中的线程支持所基于的库)。


顺便说一句,如果您不得不使用以前版本的 VC++(例如 2003),请记住,在这些版本中,标准库既有多线程版本也有单线程版本。如果您在应用程序中使用线程和 CRT 工具,则永远不应使用 CRT 的单线程版本,请始终检查项目设置以查看是否选择了正确的版本。

【讨论】:

    【解决方案2】:

    所以我猜,vector 不是线程安全的。

    当然,std::vector 不是线程安全的(没有一个标准容器保证是线程安全的——但它们的所有方法都保证在每个理智的实现中都是可重入的)。您必须使用同步机制(互斥锁等)。

    【讨论】:

      【解决方案3】:

      Vector 是一种可增长的数据结构。当然,您不能分配任意大内存,因此实​​现是分配固定大小的内存(您可以在构建时设置)。当您将 push_back 元素放入向量中时,它会填充此空间。当它到达预分配空间的末尾时,它必须分配一个更大的块(通常是原始大小的两倍)并复制其中的所有数据。它还必须更新块开始的内存位置并释放旧分配的空间。

      与此同时,您正试图将一些值写入一个不再属于您的内存地址...

      如果您知道它会增长多大,您可以预先分配它需要的所有内存。这有一个额外的好处,它不必一次又一次地复制它,因为它的大小翻了一番。

      请参阅:vector::vectorvector::capacityvector::reserve

      【讨论】:

      • 即使向量没有增长,它也包含在更新时不能保证是线程安全的内部状态。出现不良结果的机会减少了,但并未消除。
      • 不保证,但通常工作正常,猜测取决于实际实现(因为标准没有说明这一点)......它必须更新向量的大小,但存储在一个机器字,所以更新应该是原子的。
      • 更新大小可能是原子的,但您仍然会遇到麻烦。例如,假设在插入新元素之前更新大小?当实现可能随下一个编译器版本发生变化时,我不希望依赖实现细节。
      • 在这个例子中只有一个线程进行插入,所以这永远不会发生。是否有风险由您决定,我相信 STL 库经过了很好的优化,因此后台通常不会发生任何疯狂的事情。它可以满足您的期望。
      • 即使源看起来做对了,优化器仍然可能重新安排事情以创建一个小的计时窗口。如果有我讨厌的一件事,那就是它在 99.99999% 的时间内按预期工作......然后崩溃。
      猜你喜欢
      • 2015-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-27
      • 2016-04-07
      • 2021-05-22
      • 2013-12-17
      • 1970-01-01
      相关资源
      最近更新 更多