Is it concurrency-safe to call concurrency::concurrent_vector::push_back while iterating over that concurrent_vector in other thread?

pid*_*gun 9 c++ concurrency concurrent-vector

push_back, begin, end are described as concurrent safe in https://docs.microsoft.com/en-us/cpp/parallel/concrt/reference/concurrent-vector-class?view=vs-2019#push_back

However the below code is asserting. Probably because element is added but not initialized yet.

struct MyData
   {
   explicit MyData()
      {
      memset(arr, 0xA5, sizeof arr);
      }
   std::uint8_t arr[1024];
   };

struct MyVec
   {
   concurrency::concurrent_vector<MyData> v;
   };

auto vector_pushback(MyVec &vec) -> void
   {
   vec.v.push_back(MyData{});
   }

auto vector_loop(MyVec &vec) -> void
   {
   MyData myData;
   for (auto it = vec.v.begin(); it != vec.v.end(); ++it)
      {
      auto res = memcmp(&(it->arr), &(myData.arr), sizeof myData.arr);
      assert(res == 0);
      }
   }

int main()
{
   auto vec = MyVec{};
   auto th_vec = std::vector<std::thread>{};
   for (int i = 0; i < 1000; ++i)
      {
      th_vec.emplace_back(vector_pushback, std::ref(vec));
      th_vec.emplace_back(vector_loop, std::ref(vec));
      }

   for(auto &th : th_vec)
      th.join();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

And*_*dyG 3

根据文档,在迭代它时追加到 a while 应该是安全的,concurrency::concurrent_vector因为元素实际上并不是连续存储在内存中,例如std::vector

当您追加到对象或调整其大小时,对象concurrent_vector不会重新定位其元素。这使得现有的指针和迭代器在并发操作期间保持有效。

但是,查看push_backVS2017 中的实际实现,我看到以下内容,我认为这不是线程安全的:

iterator push_back( _Ty &&_Item )
{
    size_type _K;
    void *_Ptr = _Internal_push_back(sizeof(_Ty), _K);
    new (_Ptr) _Ty( std::move(_Item));
    return iterator(*this, _K, _Ptr);
}
Run Code Online (Sandbox Code Playgroud)

我必须在_Internal_push_back这里推测,但我敢打赌它会分配原始内存来存储该项目(并将最后一个元素指向这个新节点),以便下一行可以使用新的位置。我想这_Internal_push_back是内部线程安全的,但是我没有看到在安置新之前发生任何同步。意味着以下情况是可能的:

  • 获得内存并且节点“存在”(但尚未发生新的安置)
  • 循环线程遇到这个节点并执行memcmp发现它们不相等
  • 新的安置发生。

这里肯定存在竞争条件。我可以自发地重现该问题,而且我使用的线程越多。

我建议您向 Microsoft 支持开具一张票证。

  • MSFT 更新文档 https://github.com/MicrosoftDocs/cpp-docs/commit/43b6b465701b10872ea818fb15b31204081dd158#diff-30cbee232b2882b22fe4076ec6d7f4b3 (2认同)