摘录emplace_back()
的文档:
- 迭代器有效期
与此容器相关的所有迭代器都是无效的,但指针和引用仍然有效,指的是它们在调用之前引用的相同元素。
- 数据竞赛
容器已修改。
调用不会访问包含的元素:同时访问或修改它们是安全的(尽管请参阅上面的迭代器有效性)。
摘自operator[]()
的文档:
- 数据竞赛
访问容器(const和非const版本都不会修改容器)。
可能会访问或修改元素 n 。同时访问或修改其他元素是安全的。
因此,假设 deque 的某个实例至少有一个元素,通过operator[]()
访问它并同时调用容器上的emplace_back()
确实是线程安全的吗?< / p>
我倾向于说它是,但无法决定emplace_back()
的文档中“访问”是否包含operator[]()
的使用,如下所示:
int access( std::deque< int > & q )
{
return q[ 0 ];
}
void emplace( std::deque< int > & q , int i )
{
q.emplace_back( i );
}
其中两个函数同时调用,或者“access”仅适用于已经采用某些引用或指针的元素:
std::deque< int > q { 1 };
auto * ptr = & q[ 0 ]
std::thread t1 ( [ ptr ]{ * ref = 0; } );
std::thread t2 ( [ & q ]{ q.emplace_back( 2 ); } );
编辑:为了进一步参考,这里有关于引用和迭代器有效性的deque
中关于插入的C ++ 14标准(实际上是November 2014 Working Draft, N4296)的说明: / p>
- 23.3.3.4 deque modifiers
(...)
- 效果:在双端队列中间的插入使所有迭代器和对双端队列元素的引用无效。在deque两端的插入使deque的所有迭代器无效,但对deque元素的引用的有效性没有影响。
醇>(...)
答案 0 :(得分:4)
同时在标准类的对象上调用任意两个方法是不安全的,除非两者都是const
,或者除非另有说明(例如std::mutex::lock()
的情况)。我们将更详细地探讨这一点here
因此,同时使用emplace_back
和operator[]
不是安全的。但是,由于您引用的引用/指针有效性规则,您可以安全地使用之前获得的对deque
元素的引用以及对emplace_back
/ push_back
的调用,例如:
int main()
{
std::deque<int> d;
d.push_back(5);
auto &first = d[0];
auto task = std::async(std::launch::async, [&] { first=3; });
d.push_back(7);
task.wait();
for ( auto i : d )
std::cout << i << '\n';
}
这将安全输出3和7.请注意,在启动异步任务之前会创建引用first
。
答案 1 :(得分:2)
编辑注释:此答案的结论不正确[]
和emplace_back
可以同时使用。 Arne的回答是正确的。由于评论有用,请将此留在这里而不是删除。
本文档似乎说的是,尽管我并不完全信任源代码,但只要您不执行此操作,同时访问其他值是线程安全的通过迭代器。
这种情况的原因是emplace_back
不会以任何方式触及其他值。如果容量太低而无法添加其他元素,则会分配新页面。这不会影响其他元素。因此,通过其他线程使用这些值是安全的。它不会导致数据竞争,因为没有访问/修改相同的数据。
对于这种情况,容器不需要以任何方式保证线程安全。这与修改a[0]
时访问a[1]
类似。只要您正确访问/修改数据(不会导致UB),它就是一个安全的操作。您不需要任何锁保护,因为您不会同时使用相同的数据。
我更关心的是size
。这可能是由deque
修改并由emplace
读取的size
中的值。如果没有保护,这将导致数据竞争。文档没有说明这一点,只涉及元素的访问,当然可以同时调用size
。
根据this answer,除上述情况外,标准容器对螺纹安全性没有任何保证。换句话说,您可以同时访问/修改不同的元素,但其他任何因素都可能导致数据竞争。换句话说,标准容器不是线程安全的,并且不提供任何并发保护。