Apakah konkurensi aman untuk memanggil konkurensi :: concurrent_vector :: push_back sambil mengulangi concurrent_vector di utas lain?

9

push_back , begin , end digambarkan aman bersamaan di https://docs.microsoft.com/en-us/cpp/parallel/concrt/reference/concurrent-vector-class?view=vs-2019#push_back

Namun kode di bawah ini menyatakan. Mungkin karena elemen ditambahkan tetapi belum diinisialisasi.

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

Jawaban:

2

Menurut dokumen , itu harus aman untuk ditambahkan concurrency::concurrent_vectorsementara iterasi karena elemen-elemen tersebut tidak disimpan secara bersamaan dalam memori seperti std::vector:

Sebuah concurrent_vectorobjek tidak pindah elemen ketika Anda menambahkan untuk itu atau mengubah ukurannya. Ini memungkinkan pointer dan iterator yang ada untuk tetap valid selama operasi bersamaan.

Namun, melihat implementasi aktual push_backdalam VS2017, saya melihat yang berikut, yang menurut saya tidak aman untuk thread:

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

Saya harus berspekulasi di _Internal_push_backsini, tapi saya bertaruh itu mengalokasikan memori mentah untuk menyimpan item (dan menunjukkan elemen terakhir ke node baru ini) sehingga baris berikutnya dapat menggunakan emplacement baru. Saya membayangkan itu aman _Internal_push_backsecara internal, namun saya tidak melihat sinkronisasi terjadi sebelum penempatan baru. Berarti yang berikut ini dimungkinkan:

  • memori diperoleh dan node "hadir" (belum emplacement baru tidak terjadi)
  • utas perulangan bertemu simpul ini dan berkinerja memcmpuntuk menemukan bahwa mereka tidak sama
  • penempatan baru terjadi.

Jelas ada kondisi lomba di sini. Saya dapat secara spontan mereproduksi masalah, lebih banyak thread yang saya gunakan.

Saya sarankan Anda membuka tiket dengan dukungan Microsoft untuk yang satu ini.

AndyG
sumber