从std :: deque线程对emplace_back()和operator []()的并发调用是否安全?

Tar*_*arc 5 c++ multithreading thread-safety stddeque

来自emplace_back()以下文档的摘录:

  • 迭代器有效性

与此容器相关的所有迭代器都是无效的,但指针和引用仍然有效,指的是它们在调用之前引用的相同元素.

  • 数据竞赛

容器已修改.

调用不访问任何包含的元素:同时访问或修改它们是安全的(尽管请参见上面的迭代器有效性).

以及以下文档的摘录operator[]():

  • 数据竞赛

访问容器(const和非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 );
}
Run Code Online (Sandbox Code Playgroud)

其中两个函数同时调用,或者"访问"仅适用于已经采用某些引用或指针的元素:

std::deque< int > q { 1 };

auto * ptr = & q[ 0 ]

std::thread t1 ( [ ptr  ]{ * ref = 0; } );
std::thread t2 ( [ & q ]{ q.emplace_back( 2 ); } );
Run Code Online (Sandbox Code Playgroud)

编辑:为了进一步参考,这里是C++ 14标准(实际上,2014年11月的工作草案,N4296)关于deque引用和迭代器有效性的插入:

  • 23.3.3.4 deque修饰符

(......)

  1. 效果:在双端队列中间的插入使所有迭代器和对双端队列元素的引用无效.在deque两端的插入使deque的所有迭代器无效,但对deque元素的引用的有效性没有影响.

(......)

Arn*_*gel 6

在标准类的对象上同时调用任何两个方法是不安全的,除非两者都是const,或者除非另有说明(例如 的情况std::mutex::lock())。这是更详细的探讨here

因此,使用emplace_backoperator[]同时是不是安全的。但是,由于您引用的引用/指针有效性规则,您可以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';
}
Run Code Online (Sandbox Code Playgroud)

这将安全地输出 3 和 7。请注意,该引用first是在启动异步任务之前创建的。