在其他线程中迭代并发向量时调用concurrency :: concurrent_vector :: push_back是否安全并发?


9

https://docs.microsoft.com/zh-cn/cpp/parallel/concrt/reference/concurrent-vector-class?view=vs-2019#push_back中push_backbeginend被描述为并发安全

但是下面的代码是断言的。可能是因为元素已添加但尚未初始化。

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;
}

Answers:


2

根据文档,应该安全地在其上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);
}

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

  • 已获得内存,并且该节点“存在”(尚未发生新的放置)
  • 循环线程遇到此节点并执行memcmp发现它们不相等的操作
  • 安置发生了。

这里肯定有比赛条件。我可以自发地重现该问题,而且我使用的线程更多。

我建议您在这张票上获得Microsoft支持才能打开票。



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.