【发布时间】:2017-04-21 10:17:04
【问题描述】:
来自emplace_back()的文档摘录:
- 迭代器有效性
与此容器相关的所有迭代器都已失效,但指针和引用仍然有效,引用它们在调用之前引用的相同元素。
- 数据竞赛
容器被修改。
调用不会访问包含的元素:同时访问或修改它们是安全的(尽管请参阅上面的迭代器有效性)。
还有来自operator[]()的文档摘录:
- 数据竞赛
容器被访问(const 和 non-const 版本都不会修改容器)。
元素 n 可能被访问或修改。同时访问或修改其他元素是安全的。
那么,鉴于 deque 的某个实例至少有一个元素,通过operator[]() 访问它并同时在容器上调用emplace_back() 确实是线程安全的?
我倾向于说是,但无法确定emplace_back() 的文档中的“访问”是否包括使用operator[](),如下所示:
int access( std::deque< int > & q )
{
return q[ 0 ];
}
void emplace( std::deque< int > & q , int i )
{
q.emplace_back( i );
}
同时调用两个函数,或者“访问”仅适用于已获取某些引用或指针的元素:
std::deque< int > q { 1 };
auto * ptr = & q[ 0 ]
std::thread t1 ( [ ptr ]{ * ref = 0; } );
std::thread t2 ( [ & q ]{ q.emplace_back( 2 ); } );
编辑:为了进一步参考,以下是 C++ 14 标准(实际上是 November 2014 Working Draft, N4296)关于在 deque 中插入关于引用和迭代器有效性的声明:
- 23.3.3.4 双端队列修饰符
(...)
- 效果:在双端队列中间插入会使所有迭代器和对双端队列元素的引用无效。在双端队列的任一端插入都会使双端队列的所有迭代器无效,但不会影响对双端队列元素的引用的有效性。
(...)
【问题讨论】:
-
没有。
deque不是线程安全的容器。这里emplace_back()可能导致重新分配,这几乎关闭了任何类型的线程安全访问容器的书籍。 -
@SamVarshavchik -
emplace_back永远不会导致deque中的重新分配。它可能分配一个新页面。即使它确实导致了重新分配,也没有什么说不能使用互斥锁来确保正确的并发访问。 STL 容器关于并发的问题是back和pop_back。那些不能被线程安全地拉出并保留它的值。它需要外部锁。front/pop_front也一样。 -
emplace_back更改内容,operator[]读取它们(读取至少一个指向数据的指针)。这些操作不保证是原子的。因此,它不是线程安全的。
标签: c++ multithreading thread-safety stddeque