2010-03-18 4 views
30

Các toán tử "sửa đổi" như +=, |=, &= v.v ... có phải là nguyên tử không?Là + =, | =, & = v.v ... nguyên tử?

Tôi biết ++ là nguyên tử (nếu bạn thực hiện x++; trong hai chủ đề khác nhau "đồng thời", bạn sẽ luôn luôn kết thúc với x tăng 2, như trái ngược với x=x+1 với tối ưu hóa tắt.)

Những gì tôi tự hỏi là liệu variable |= constant và các lượt thích có an toàn theo chủ đề không hoặc tôi có phải bảo vệ chúng bằng một mutex không?

(... hoặc có phụ thuộc vào CPU không? Trong trường hợp này, làm thế nào trên ARM?)

+1

ARM nào? Kiến trúc v6 (ARM10) và sau đó có thể cung cấp các hoạt động nguyên tử, nếu trình biên dịch hỗ trợ nó hoặc bạn cuộn lắp ráp của riêng bạn. Kiến trúc trước đó không thể. –

+0

gcc đã tích hợp hoạt động nguyên tử: http://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Atomic-Builtins.html#Atomic-Builtins; xin lưu ý: "Không phải tất cả các hoạt động đều được hỗ trợ bởi tất cả các bộ vi xử lý đích" – Christoph

+0

có API Windows để truy cập biến khóa liên động: http://msdn.microsoft.com/en-us/library/ms684122%28v=VS.85%29.aspx –

Trả lời

72

Bạn đang sai. Không có sự đảm bảo rằng ++ là nguyên tử và không có ở đó cho các toán tử gán phép, hoặc thực sự cho bất kỳ hoạt động C++ nào.

+3

Điều này có nghĩa là CPU cụ thể. Trên các kiến ​​trúc lõi đơn cho phép 'inc [address]', điều này chắc chắn là nguyên tử. –

+1

Ít nhất là không cho đến khi C++ 0x: "Điều khoản này mô tả các thành phần cho truy cập nguyên tử hạt mịn. Truy cập này được cung cấp thông qua các hoạt động trên các đối tượng nguyên tử." [29.1/1 trong bản nháp n3035]. –

+24

@SF Không phải vậy. Nó là trình biên dịch cụ thể.Chỉ vì kiến ​​trúc CPU có một lệnh không có nghĩa là trình biên dịch sẽ sử dụng nó theo cách bạn nghĩ, nếu thực sự nó sử dụng nó. –

6

Để thay đổi giá trị hiển thị trên các lõi, một + = (ví dụ) sẽ phải tải giá trị, thêm gia số và sau đó lưu trữ giá trị đó. Điều này có nghĩa là hoạt động sẽ không phải là nguyên tử.

Để đảm bảo nguyên tử bạn cần đặt khóa thích hợp xung quanh thao tác.

2

++ có thể là nguyên tử trên trình biên dịch/nền tảng của bạn, nhưng trong thông số C++, nó không được định nghĩa là nguyên tử.

Nếu bạn muốn đảm bảo sửa đổi một giá trị theo cách nguyên tử, bạn nên sử dụng các phương pháp appropiate, như Interlocked * trên cửa sổ.

Tương tự cho tất cả các thói quen khác. Nếu bạn muốn hoạt động nguyên tử, bạn nên sử dụng các cuộc gọi appropiate, chứ không phải các cuộc gọi tiêu chuẩn.

2

Không có nhà điều hành nào trong C hoặc C++ được đảm bảo là nguyên tử. Họ có thể là trên nền tảng của bạn, nhưng bạn sẽ không biết chắc chắn. Thông thường, hoạt động duy nhất là nguyên tử là một lệnh Test và Set, thường có sẵn trên hầu hết các CPU hiện đại dưới dạng một số làm cơ sở để thực hiện các semaphores.

9

x++ thường được thực hiện theo 3 hướng dẫn: Đọc X vào sổ đăng ký, Tăng và ghi lại bộ nhớ.

Chủ đề của bạn có thể được đặt trước ở giữa bất kỳ chủ đề nào.

+1

Trên một số bộ vi xử lý, ngay cả những hướng dẫn đơn lẻ cũng có thể không nguyên tử. –

0

Điều đáng nói đến là các toán tử này có thể bị quá tải, vì vậy chắc chắn không có sự đảm bảo chung nào là chúng nguyên tử cho tất cả các lớp.

+1

Chúng không thể bị quá tải đối với các số nguyên, mà tôi nghĩ đó là câu hỏi. –

+0

Đồng ý, nhưng nó không nói rằng, đó là lý do tại sao tôi nghĩ nó đáng nói đến. – Oddthinking

0

Ngay cả khi ++ là một hoạt động nguyên tử, không hàm ý rằng hai chủ đề làm ++x sẽ cho kết quả là x chính xác là cao hơn hai. Bạn phải đồng bộ hóa các chủ đề bằng cách nào đó, hoặc nếu không chúng sẽ không nhất thiết phải nhìn thấy những thay đổi của nhau.

0

Bạn phải bảo vệ biến của mình, với một mutex chẳng hạn

2

Cả trình biên dịch và CPU đều phụ thuộc. Một số bộ hướng dẫn cung cấp các hướng dẫn nguyên tử cho những (trên ints máy có kích thước).

Tuy nhiên, không có gì đảm bảo rằng trình biên dịch của bạn sẽ sử dụng các hướng dẫn đó và sẽ không tối ưu hóa mã của bạn theo cách không an toàn. Bạn cần phải viết thường trình trong assembly hoặc sử dụng một kỹ thuật biên dịch cụ thể (chẳng hạn như instrinsics) cung cấp nguyên tử (hoặc sử dụng một thư viện sử dụng một trong những kỹ thuật đó).


Cụ thể trên ARM: Các ORR/ADD/VÀ hướng dẫn lấy hai toán hạng và đặt kết quả trong một thanh ghi. Toán hạng có thể là thanh ghi tương tự như thanh ghi kết quả, vì vậy chúng có thể được sử dụng như một nguyên tử | =, + =, & =.

Tất nhiên, kết quả được đặt trong sổ đăng ký và toán hạng đầu tiên cũng phải đến từ sổ đăng ký, vì vậy bạn phải đảm bảo tải đăng ký được thực hiện một cách nguyên tử.

1

Không chỉ là chúng không nguyên tử, giống như tất cả các hoạt động, nhưng chúng có thể có kết quả rất thú vị. Ví dụ, nếu trình biên dịch thấy rằng nó ghi vào x, nó được phép sử dụng x như một biến tạm thời, thay vì sử dụng các thanh ghi hoặc không gian ngăn xếp. Điều này có nghĩa x có thể tạm thời chứa bất kỳ giá trị, không chỉ là giá trị mà có ý nghĩa cho x

http://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong

1

Một trùng lặp được đạo diễn ở đây, và điều này cần một bản cập nhật. Ngôn ngữ C11 “mới” cho phép thuộc tính nguyên tử thừa nhận rằng:

_Atomic int a; 
... 
a += 3 

có thể được biên dịch thành vòng lặp không có nguyên tử. Cảm ơn những người tiêu chuẩn quà tặng, tôi thực sự mong bạn không có.

1: trong một số kiến ​​trúc, hoạt động nguyên tử chỉ có thể thực hiện trên bộ nhớ hỗ trợ các giao thức truy cập nhất định. Ví dụ: ARMv7, MIPS chuyển chuỗi thành:

do { 
    x = LoadLinked(a) + 3; 
} while !StoreConditional(x, &a); 

nhưng LoadLinked/StoreĐiều kiện không xác định đối với một số loại bộ nhớ/bộ nhớ cache. Thưởng thức gỡ lỗi đó.

2: liên quan là chia sẻ sai là một tạo tác của LoadLinked, StoreConditional hoạt động trên các dòng bộ nhớ cache (ví dụ 32, 64, 256 byte) không phải khối phụ. Vì vậy: _Atomic int a [4]; có thể yêu cầu 4 * kích thước dòng bộ nhớ cache (do đó 1024 byte) để cho phép đồng thời hoạt động nguyên tử đồng thời trên [n] và [n + 1], vì 4 cpu có thể bị mờ để cập nhật [0..3], nhưng không bao giờ thành công.

Hy vọng tiêu chuẩn tiếp theo sẽ nhận ra sự thất bại vốn có của trang trí thuộc tính và khôi phục c89 làm chuẩn C đúng.