Có an toàn khi gọi concurrency :: concurrency_vector :: push_back trong khi lặp lại trên conc conc_vector đó trong luồng khác không?


9

push_back , bắt đầu , cuối được mô tả như an toàn đồng thời trong https://docs.microsoft.com/en-us/cpp/parallel/concrt/reference/concurrent-vector-class?view=vs-2019#push_back

Tuy nhiên, đoạn mã dưới đây đang khẳng định. Có lẽ bởi vì phần tử được thêm vào nhưng chưa được khởi tạo.

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

Câu trả lời:


2

Theo các tài liệu , sẽ an toàn khi nối thêm một concurrency::concurrent_vectorlúc lặp lại vì các phần tử không thực sự được lưu trữ liên tục trong bộ nhớ như std::vector:

Một concurrent_vectorđối tượng không di chuyển các phần tử của nó khi bạn nối vào nó hoặc thay đổi kích thước của nó. Điều này cho phép các con trỏ và bộ lặp hiện có vẫn còn hiệu lực trong các hoạt động đồng thời.

Tuy nhiên, nhìn vào triển khai thực tế push_backtrong VS2017, tôi thấy những điều sau đây, điều mà tôi không nghĩ là an toàn cho chuỗi:

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

Tôi phải suy đoán _Internal_push_backở đây, nhưng tôi muốn nó phân bổ bộ nhớ thô để lưu trữ vật phẩm (và chỉ phần tử cuối cùng vào nút mới này) để dòng tiếp theo có thể sử dụng vị trí mới. Tôi tưởng tượng rằng nó _Internal_push_backan toàn trong luồng, tuy nhiên tôi không thấy bất kỳ sự đồng bộ hóa nào xảy ra trước khi vị trí mới. Có nghĩa như sau là có thể:

  • bộ nhớ được lấy và nút là "hiện tại" (tuy nhiên vị trí mới không xảy ra)
  • luồng lặp gặp nút này và thực hiện memcmpđể phát hiện ra rằng chúng không bằng nhau
  • vị trí mới xảy ra.

Chắc chắn có một điều kiện cuộc đua ở đây. Tôi có thể tự động tái tạo vấn đề, thay vào đó tôi sử dụng nhiều chủ đề hơn.

Tôi khuyên bạn nên mở một vé với sự hỗ trợ của Microsoft về điều này.



1
MSFT đã cập nhật tài liệu github.com/MicrosoftDocs/cpp-docs/commit/NH
pidgun
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.