在MacOSX上,使用g++编译器,std::vector.size()方法是否是线程安全的?

7

我有一个std::vector<...>被两个线程共享。

它们都调用vec->size();

这可能是竞争条件的源吗?我希望不是,因为vec->size()是const。

谢谢!


1
FYI:在Mac OS X上,gcc 4.2上的size()实现为size_type size() const { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); } - kennytm
你在哪里找到这个的?请告诉我一下。 - anon
标准头文件位于“/usr/include/c++/*”下,从您实际包含的“<vector>”开始阅读所包含的文件,直到找到您要查找的内容为止。“<bits/stl_vector.h>”是g++用于存储实际向量实现的常见头文件(带有“<bits/stl_bvector.h>”作为“bool”专业化,以及用于实用函数的其他头文件)。 - David Rodríguez - dribeas
2个回答

5

如果您只调用 vec->size() 方法,则是安全的。但这很难相信。一旦调用了任何更改方法,例如 push_back ,竞争就可能导致获得错误的大小。


2
不难相信,因为...我想有人可以检查vector.size()并且如果不为空,那么可以进行互斥锁定/解锁。 - Vassilis

3
也许不会。问题不仅在于vec->size(),而且也涉及到其他所有函数。
考虑这种情况:vector::size()通常是直接从成员计算出来的,例如.end - .begin。那么当一个线程执行push_back操作时会发生什么呢?它显然会通过成员影响大小并改变内存。但是没有内存屏障。其他核心上的线程将只看到旧的内存。因此,当它们调用size()时,它将使用旧值进行计算。
一个明显的例外是,在创建线程后,如果向量的大小不变,则线程永远不会有过时的信息。

我完全同意这一点。当你编写多线程代码时,应该尽量使你的操作原子化。即使这个操作本身是安全的;如果你正在为其他操作使用互斥锁或关键部分,你也必须为此使用它们。如果你担心性能问题,可以使用读写锁来允许多个读者调用size(),但只允许一个写者。 - iain
实际上 size() 方法并不访问成员。它不会导致崩溃,最糟糕的情况是得到一个过时的结果。请看我的回答。在某些情况下,拥有过时的值是可以接受的,比如将容器大小表示为进度条展示给用户。这是可以的,因为你不需要迭代访问容器,而且如果该值过时,它将在下一次刷新显示时被更新。 - Aurélien
2
@Aurelien:你所声称的直接与KennyTM的第一条评论相矛盾,该评论清楚地显示gcc 4.2访问了“_M_finish”和“_M_start”成员。如果另一个线程重新分配向量并从新的“_M_finish”中减去旧的“_M_start”,那么可能会产生负数大小。将该负结果转换回“size_type”,您将得到一个荒谬的大值。是的,绘制一个40亿像素宽的进度条可能会引起一些麻烦。 - MSalters
是的,你说得对。我显然误解了文档中的那句话:“不访问包含的元素:并发访问或修改它们是安全的。”这可能只意味着 size() 对于容器中已经存在的元素的并发修改是线程安全的。这并不意味着它对于修改元素数量是线程安全的。谢谢,我会删除我的回答。 - Aurélien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接