Tôi đã nghe và đọc một số bài báo, các bài nói chuyện và các câu hỏi về stackoverflow std::atomic
, và tôi muốn chắc chắn rằng tôi đã hiểu rõ về nó. Bởi vì tôi vẫn còn một chút nhầm lẫn với khả năng ghi dòng bộ đệm do sự chậm trễ có thể xảy ra trong các giao thức kết nối bộ đệm MESI (hoặc dẫn xuất), lưu trữ bộ đệm, hàng đợi không hợp lệ, v.v.
Tôi đọc x86 có mô hình bộ nhớ mạnh hơn và nếu việc vô hiệu hóa bộ đệm bị trì hoãn thì x86 có thể hoàn nguyên các hoạt động đã bắt đầu. Nhưng bây giờ tôi chỉ quan tâm đến những gì tôi nên giả sử là một lập trình viên C ++, độc lập với nền tảng.
[T1: thread1 T2: thread2 V1: biến nguyên tử dùng chung]
Tôi hiểu rằng std :: nguyên tử đảm bảo rằng,
(1) Không có cuộc đua dữ liệu nào xảy ra trên một biến (nhờ truy cập độc quyền vào dòng bộ đệm).
(2) Tùy thuộc vào bộ nhớ mà chúng tôi sử dụng, nó đảm bảo (với các rào cản) rằng tính nhất quán tuần tự xảy ra (trước một rào cản, sau một rào cản hoặc cả hai).
(3) Sau khi ghi nguyên tử (V1) trên T1, một nguyên tử RMW (V1) trên T2 sẽ được kết hợp (dòng bộ đệm của nó sẽ được cập nhật với giá trị ghi trên T1).
Nhưng như đề cập đến bộ đệm kết hợp bộ đệm ,
Hàm ý của tất cả những điều này là, theo mặc định, tải có thể tìm nạp dữ liệu cũ (nếu một yêu cầu vô hiệu tương ứng đang ngồi trong hàng đợi không hợp lệ)
Vậy, điều nào sau đây là đúng?
(4) std::atomic
KHÔNG đảm bảo rằng T2 sẽ không đọc giá trị 'cũ' trên số đọc nguyên tử (V) sau khi viết nguyên tử (V) trên T1.
Câu hỏi nếu (4) là đúng: nếu nguyên tử ghi trên T1 làm mất hiệu lực dòng bộ đệm bất kể độ trễ, tại sao T2 chờ hiệu lực có hiệu lực khi hoạt động của RMW nguyên tử nhưng không đọc trên nguyên tử?
Câu hỏi nếu (4) sai: khi nào thì một chủ đề có thể đọc giá trị 'cũ' và "nó hiển thị" trong khi thực hiện, sau đó?
Tôi đánh giá cao câu trả lời của bạn rất nhiều
Cập nhật 1
Vì vậy, có vẻ như tôi đã sai trên (3) rồi. Hãy tưởng tượng xen kẽ sau đây, với giá trị ban đầu là V1 = 0:
T1: W(1)
T2: R(0) M(++) W(1)
Mặc dù RMW của T2 được đảm bảo xảy ra hoàn toàn sau W (1) trong trường hợp này, nó vẫn có thể đọc giá trị 'cũ' (tôi đã sai). Theo đó, nguyên tử không đảm bảo tính đồng nhất bộ nhớ cache đầy đủ, chỉ có tính nhất quán tuần tự.
Cập nhật 2
(5) Bây giờ hãy tưởng tượng ví dụ này (x = y = 0 và là nguyên tử):
T1: x = 1;
T2: y = 1;
T3: if (x==1 && y==0) print("msg");
theo những gì chúng ta đã nói, việc nhìn thấy "thông điệp" được hiển thị trên màn hình sẽ không cung cấp cho chúng ta thông tin ngoài việc T2 được thực hiện sau T1. Vì vậy, một trong những vụ hành quyết sau đây có thể đã xảy ra:
- T1 <T3 <T2
- T1 <T2 <T3 (trong đó T3 thấy x = 1 nhưng chưa có y = 1)
Có đúng không?
(6) Nếu một luồng luôn có thể đọc các giá trị 'cũ', điều gì sẽ xảy ra nếu chúng ta lấy kịch bản "xuất bản" điển hình nhưng thay vì báo hiệu rằng một số dữ liệu đã sẵn sàng, chúng ta sẽ làm ngược lại (xóa dữ liệu)?
T1: delete gameObjectPtr; is_enabled.store(false, std::memory_order_release);
T2: while (is_enabled.load(std::memory_order_acquire)) gameObjectPtr->doSomething();
trong đó T2 vẫn sẽ sử dụng ptr bị xóa cho đến khi thấy is_enables là sai.
(7) Ngoài ra, thực tế là các luồng có thể đọc các giá trị 'cũ' có nghĩa là một mutex có thể được thực hiện chỉ với một nguyên tử không khóa phải không? Nó sẽ đòi hỏi một cơ chế đồng bộ giữa các chủ đề. Nó sẽ yêu cầu một nguyên tử có thể khóa?