Đọc và ghi trong C ++ có phải là int Atomic không?


81

Tôi có hai luồng, một đang cập nhật một int và một đang đọc nó. Đây là một giá trị thống kê trong đó thứ tự của các lần đọc và ghi là không liên quan.

Câu hỏi của tôi là, tôi có cần phải đồng bộ hóa quyền truy cập vào giá trị nhiều byte này không? Hoặc, nói một cách khác, một phần của bài viết có thể hoàn chỉnh và bị gián đoạn, và sau đó việc đọc diễn ra.

Ví dụ: hãy nghĩ về một giá trị = 0x0000FFFF nhận giá trị tăng dần là 0x00010000.

Có lúc nào mà giá trị giống như 0x0001FFFF mà tôi nên lo lắng không? Chắc chắn là loại càng lớn thì càng có thể xảy ra chuyện như thế này.

Tôi luôn đồng bộ hóa các loại quyền truy cập này, nhưng tò mò không biết cộng đồng nghĩ gì.


5
Có thật không? Tôi không quan tâm cộng đồng nghĩ gì. Tôi sẽ quan tâm sự thật là gì :)
xem

1
Đọc thú vị về chủ đề: channel9.msdn.com/Shows/Going+Deep/…
eOn

Câu trả lời:


47

Lúc đầu, người ta có thể nghĩ rằng việc đọc và ghi kích thước máy gốc là nguyên tử nhưng có một số vấn đề cần giải quyết bao gồm đồng tiền bộ nhớ đệm giữa các bộ xử lý / lõi. Sử dụng các hoạt động nguyên tử như Interlocked * trên Windows và tương đương trên Linux. C ++ 0x sẽ có một mẫu "nguyên tử" để bọc chúng trong một giao diện đa nền tảng và đẹp mắt. Hiện tại nếu bạn đang sử dụng một lớp trừu tượng nền tảng, nó có thể cung cấp các chức năng này. ACE nào có thì xem mẫu lớp ACE_Atomic_Op .


Tài liệu của ACE_Atomic_Op đã được chuyển đi - hiện có thể tìm thấy tài liệu này tại dre.vanderbilt.edu/~schmidt/DOC_ROOT/ACE/ace/Atomic_Op.inl
Byron

63

Cậu bé, đúng là một câu hỏi. Câu trả lời là:

Vâng, không, hmmm, tốt, nó phụ thuộc

Tất cả đều phụ thuộc vào kiến ​​trúc của hệ thống. Trên IA32, một địa chỉ được căn chỉnh chính xác sẽ là một phép toán nguyên tử. Các ghi không có dấu có thể là nguyên tử, nó phụ thuộc vào hệ thống bộ nhớ đệm được sử dụng. Nếu bộ nhớ nằm trong một dòng cache L1 thì nó là nguyên tử, ngược lại thì không. Chiều rộng của bus giữa CPU và RAM có thể ảnh hưởng đến bản chất nguyên tử: ghi 16 bit được căn chỉnh chính xác trên 8086 là nguyên tử trong khi ghi tương tự trên 8088 thì không phải vì 8088 chỉ có bus 8 bit trong khi 8086 có Xe buýt 16 bit.

Ngoài ra, nếu bạn đang sử dụng C / C ++, đừng quên đánh dấu giá trị được chia sẻ là dễ bay hơi, nếu không trình tối ưu hóa sẽ nghĩ rằng biến không bao giờ được cập nhật trong một trong các chuỗi của bạn.


23
Các từ khóa dễ bay hơi không phải là hữu ích trong chương trình đa luồng stackoverflow.com/questions/2484980/...

5
@IngeHenriksen: Tôi không bị thuyết phục bởi liên kết đó.
Skizz

một nguồn khác, nhưng rất tiếc là rất cũ (nó có trước std :: atom): web.archive.org/web/20190219170904/https://software.intel.com/…
Max Barraclough

11

NẾU bạn đang đọc / ghi giá trị 4 byte VÀ nó được căn chỉnh theo DWORD trong bộ nhớ VÀ bạn đang chạy trên kiến ​​trúc I32, thì việc đọc và ghi là nguyên tử.


2
Điều này được nêu ở đâu trong sổ tay của nhà phát triển phần mềm kiến ​​trúc Intel?
Daniel Trebbien

2
@DanielTrebbien: có lẽ xem stackoverflow.com/questions/5002046/…
xem

9

Có, bạn cần phải đồng bộ hóa các quyền truy cập. Trong C ++ 0x, nó sẽ là một cuộc đua dữ liệu và hành vi không xác định. Với các luồng POSIX, nó đã là hành vi không xác định.

Trong thực tế, bạn có thể nhận được các giá trị không hợp lệ nếu kiểu dữ liệu lớn hơn kích thước từ gốc. Ngoài ra, một luồng khác có thể không bao giờ thấy giá trị được ghi do các tối ưu hóa di chuyển đọc và / hoặc ghi.


3

Bạn phải đồng bộ hóa, nhưng trên một số kiến ​​trúc nhất định có những cách hiệu quả để thực hiện.

Tốt nhất là sử dụng các chương trình con (có thể được che sau macro) để bạn có thể thay thế các triển khai có điều kiện bằng các chương trình dành riêng cho nền tảng.

Nhân Linux đã có một số mã này.


3

Trên Windows, Interlocked *** Exchange *** Add được đảm bảo là nguyên tử.


2

Để lặp lại những gì mọi người đã nói ở trên, ngôn ngữ trước C ++ 0x không thể đảm bảo bất cứ điều gì về quyền truy cập bộ nhớ được chia sẻ từ nhiều luồng. Mọi đảm bảo sẽ tùy thuộc vào trình biên dịch.


0

Không, họ không (hoặc ít nhất bạn không thể cho rằng họ là như vậy). Phải nói rằng, có một số thủ thuật để thực hiện điều này một cách nguyên tử, nhưng chúng thường không di động (xem So sánh và hoán đổi ).


0

Tôi đồng ý với nhiều người và đặc biệt là Jason . Trên windows, người ta có thể sử dụng InterlockedAdd và những người bạn của nó.


0

Ngoài vấn đề bộ nhớ cache được đề cập ở trên ...

Nếu bạn chuyển mã sang bộ xử lý có kích thước thanh ghi nhỏ hơn, nó sẽ không còn là nguyên tử nữa.

IMO, các vấn đề về luồng quá khó để có thể mạo hiểm.


0

Cách di động duy nhất là sử dụng kiểu sig_atomic_t được xác định trong tiêu đề signal.h cho trình biên dịch của bạn. Trong hầu hết các triển khai C và C ++, đó là một int. Sau đó, khai báo biến của bạn là "biến sig_atomic_t."


dễ bay hơi không làm những gì bạn nghĩ rằng nó stackoverflow.com/questions/2484980/...
Sam Miller

0

Hãy lấy ví dụ này

int x;
x++;
x=x+5;

Câu lệnh đầu tiên được giả định là nguyên tử vì nó chuyển thành một chỉ thị hợp ngữ INC đơn mà thực hiện một chu kỳ CPU. Tuy nhiên, nhiệm vụ thứ hai yêu cầu một số phép toán nên rõ ràng nó không phải là một phép toán nguyên tử.

Một ví dụ khác,

x=5;

Một lần nữa, bạn phải tháo rời mã để xem chính xác điều gì xảy ra ở đây.


2
Nhưng trình biên dịch có thể tối ưu hóa nó thành x+=6.
tc.

0

tc, tôi nghĩ rằng thời điểm bạn sử dụng một hằng số (như 6), hướng dẫn sẽ không được hoàn thành trong một chu kỳ máy. Hãy thử xem tập lệnh của x + = 6 so với x ++


0

Một số người nghĩ rằng ++ c là nguyên tử, nhưng hãy để ý đến assembly được tạo ra. Ví dụ với 'gcc -S':

movl    cpt.1586(%rip), %eax
addl    $1, %eax
movl    %eax, cpt.1586(%rip)

Để tăng một số int, trước tiên trình biên dịch sẽ tải nó vào một thanh ghi và lưu trữ nó trở lại bộ nhớ. Đây không phải là nguyên tử.


1
Đây không phải là vấn đề nếu chỉ có một luồng đang ghi vào biến, vì không có hiện tượng xé.
Ben Voigt

0

Chắc chắn là KHÔNG! Câu trả lời đó từ cơ quan C ++ cao nhất của chúng tôi, M. Boost:
Các hoạt động trên các biến "thông thường" không được đảm bảo là nguyên tử.


2
liên kết mà chỉ nói arithmetichoạt động trong đó bao gồm một chuỗi đọc update-ghi trên biến 'bình thường' không phải là nguyên tử, không phải là liệu readhoặc writehoạt động trên các biến 'thông thường' là nguyên tử hay không.
D3Hunter
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.