2013-06-01 55 views
34

Tôi đọc qua Anthony Williams' "C++ Concurrency in Action" và trong Chương 5, mà nói về mô hình bộ nhớ multithreading-aware mới và các hoạt động nguyên tử, và ông khẳng định:Liệu std :: nguyên tử <std::string> có hoạt động phù hợp không?

Để sử dụng std::atomic<UDT> cho một số người dùng định nghĩa UDT, loại này phải có một nhà điều hành gán bản sao tầm thường.

Theo tôi được biết, điều này có nghĩa rằng chúng ta có thể sử dụng std::atomic<UDT> nếu lợi nhuận sau đúng:

std::is_trivially_copyable<UDT>::value 

Theo logic này, chúng ta không nên có thể sử dụng std::string làm mẫu đối số cho std::atomic và nó hoạt động chính xác.

Tuy nhiên, đoạn mã sau biên dịch và chạy với sản lượng dự kiến:

#include <atomic> 
#include <thread> 
#include <iostream> 
#include <string> 

int main() 
{ 
    std::atomic<std::string> atomicString; 

    atomicString.store("TestString1"); 

    std::cout << atomicString.load() << std::endl; 

    atomicString.store("TestString2"); 

    std::cout << atomicString.load() << std::endl; 

    return 0; 
} 

Đây có phải là một trường hợp hành vi undefined mà chỉ xảy ra để hành xử như mong đợi?

Cảm ơn trước!

+1

Trình biên dịch của bạn (và việc triển khai stdlib) của bạn là gì? Tôi không thể làm cho nó trình biên dịch [ở đây] (http://coliru.stacked-crooked.com/view?id=0ce3b66093e9a0a59d5179429373eea7-e54ee7a04e4b807da0930236d4cc94dc), và thực sự đó là những gì tôi đã mong đợi –

+0

@AndyProwl Tôi đang sử dụng VS 2012 tại thời điểm, không có CTP tháng 11. –

+2

Khi bạn đang sử dụng nó, tôi sẽ không mong đợi để xem một vấn đề. Vấn đề sẽ phát sinh khi hai (hoặc nhiều hơn) chủ đề đã cố gắng để sửa đổi cùng một chuỗi cùng một lúc. Tại thời điểm đó, toán tử non-tầm thường của 'string' sẽ bắt đầu gây ra vấn đề. Chỉ cần gói một cái gì đó trong 'std :: atomic' không có khả năng phá vỡ mã mà sẽ là tốt mà không có nó. Đồng thời, không tuân theo các quy tắc của nó, nó sẽ không giúp mã mà sẽ bị phá vỡ mà không có nó. –

Trả lời

38

Tiêu chuẩn không chỉ định chuyên môn của std::atomic<std::string>, do đó, áp dụng chung template <typename T> std::atomic<T>. 29.5 [atomics.types.generic] p1 trạng thái:

Có một mẫu lớp nguyên tử chung. Loại đối số mẫu T sẽ được sao chép một cách trivially (3.9).

Không có tuyên bố rằng việc triển khai phải chẩn đoán vi phạm yêu cầu này. Vì vậy, hoặc (a) việc bạn sử dụng std::atomic<std::string> gọi hành vi không xác định hoặc (b) việc triển khai của bạn cung cấp std::atomic<std::string> làm tiện ích mở rộng phù hợp.

Xem trang MSDN cho std::atomic<T> (http://msdn.microsoft.com/en-us/library/vstudio/hh874651.aspx), nó đề cập rõ ràng yêu cầu T có thể sao chép một cách trivially và nó KHÔNG nói bất cứ điều gì cụ thể về std::atomic<std::string>. Nếu nó là một phần mở rộng, nó không có giấy tờ. Tiền của tôi là hành vi không xác định.

Cụ thể, 17.6.4.8/1 áp dụng (with thanks to Daniel Krügler for setting me straight):

Trong những trường hợp nhất định (chức năng thay thế, chức năng xử lý, hoạt động trên các loại sử dụng để nhanh chóng thành phần thư viện mẫu tiêu chuẩn), Thư viện C++ chuẩn phụ thuộc vào các thành phần được cung cấp bởi một chương trình C++. Nếu các thành phần này không đáp ứng các yêu cầu của chúng, Tiêu chuẩn đặt ra không có yêu cầu về việc thực hiện.

std::string chắc chắn không đáp ứng được yêu cầu std::atomic<T> rằng tham số T mẫu được trivially copyable, vì vậy những nơi tiêu chuẩn không có yêu cầu về việc thực hiện. Là vấn đề về chất lượng triển khai, lưu ý rằng static_assert(std::is_trivially_copyable<T>::value, "std::atomic<T> requires T to be trivially copyable"); là một chẩn đoán dễ dàng để bắt vi phạm này.


2016-04-19 Cập nhật: Tôi không biết khi nào sự thay đổi xảy ra, nhưng bây giờ VS2015 Cập nhật 2 không chẩn đoán std::atomic<std::string>:

error C2338: atomic requires T to be trivially copyable.
0

Tại sao bạn nghĩ điều này sẽ làm việc 'chính xác' khi có nhiều chủ đề cố đọc/ghi std::atomic<std::string>?

Đây là C++, bạn chắc chắn được phép tự bắn mình vào chân. Nếu bạn muốn sử dụng một loại không đáp ứng các yêu cầu mà bạn được tự do sử dụng, trình biên dịch "có thể" (sẽ không!) Dừng bạn, nhưng bạn sẽ bắt đầu thấy hành vi lạ/không giải thích được tại một số điểm khi nhiều chủ đề cố gắng đọc/viết chuỗi.

Yêu cầu này là để đảm bảo tính nguyên tử của số lần đọc và ghi, nếu đối tượng không thể sao chép một cách dễ dàng, sau đó trực quan hóa cảnh này: Chuỗi có "Giá trị cũ" trong đó. 1 Writer vấn đề .store ("Dữ liệu mới"), bây giờ có một luồng khác có vấn đề .load() trên cùng một biến, bây giờ không có thuộc tính trivially_copyable, chuỗi trình đọc có thể thấy "Giá trị Nld" hoặc "Giá trị mới", v.v. Nó không thể được cập nhật một cách nguyên tử, do đó có kết quả lạ.

Vì ví dụ bạn đăng là mã tuần tự, điều này không xảy ra.

6

Không, đây là hành vi không xác định. Hơn nữa, vì std :: string là không trivially copyable, phù hợp với trình biên dịch nên đã ban hành "ít nhất một thông điệp chẩn đoán":

29,5 loại Atomic

Có một lớp mẫu chung nguyên tử. Loại đối số mẫu T sẽ được sao chép một cách trivially (3.9).

1,4 tuân thủ thực hiện

- Nếu một chương trình chứa một vi phạm bất kỳ quy tắc có thể chẩn đoán [...] một phù hợp thực hiện ban hành ít nhất một thông điệp chẩn đoán.

Các vấn đề liên quan