2010-01-16 35 views
7

C bang # đặc tả trong mục 5.5 mà đọc và viết trên một số loại (cụ thể là bool, char, byte, sbyte, short, ushort, uint, int, float, và tham khảo các loại) có bảo đảm là nguyên tử.Làm thế nào để đảm bảo nguyên tử của các hoạt động đọc/ghi?

Điều này đã khơi gợi sự quan tâm của tôi. Làm thế nào bạn có thể làm điều đó? Ý tôi là, kinh nghiệm cá nhân của tôi chỉ cho thấy tôi khóa các biến hoặc sử dụng các rào cản nếu tôi muốn đọc và viết để trông nguyên tử; đó sẽ là một kẻ giết người hiệu suất nếu nó phải được thực hiện cho mỗi lần đọc/ghi đơn. Tuy nhiên, C# làm một cái gì đó với một hiệu ứng tương tự.

Có lẽ các ngôn ngữ khác (như Java) làm điều đó. Tôi nghiêm túc không biết. Câu hỏi của tôi không thực sự có ý định ngôn ngữ cụ thể, nó chỉ là tôi biết C# hiện nó.

Tôi hiểu rằng nó có thể phải đối phó với một số hướng dẫn xử lý cụ thể nhất định và có thể không sử dụng được trong C/C++. Tuy nhiên, tôi vẫn muốn biết nó hoạt động như thế nào.

[EDIT] Để nói sự thật, tôi tin rằng việc đọc và ghi có thể không nguyên tử trong một số điều kiện nhất định, như CPU ​​có thể truy cập vị trí bộ nhớ trong khi CPU khác đang viết ở đó. Điều này chỉ xảy ra khi CPU không thể xử lý tất cả đối tượng cùng một lúc, giống như vì nó quá lớn hoặc do bộ nhớ không được căn chỉnh trên ranh giới phù hợp?

Trả lời

13

Lý do các loại đó đảm bảo nguyên tử là vì chúng đều có kích thước 32 bit hoặc nhỏ hơn. Vì .NET chỉ chạy trên các hệ điều hành 32 và 64 bit, kiến ​​trúc bộ vi xử lý có thể đọc và ghi toàn bộ giá trị trong một thao tác đơn lẻ. Điều này trái ngược với việc nói, một Int64 trên nền tảng 32 bit phải được đọc và viết bằng cách sử dụng hai hoạt động 32 bit.

Tôi không thực sự là một người phần cứng nên tôi xin lỗi nếu thuật ngữ của tôi làm cho tôi có vẻ giống như một bộ phim nhưng đó là ý tưởng cơ bản.

+1

Với tôi, âm thanh thực sự rõ ràng và dễ hiểu. – Dykam

+0

Meh. Tôi bằng cách nào đó nghĩ rằng nó có thể cho một thread để truy cập vào một vị trí bộ nhớ _during_ hoạt động viết của CPU khác, dẫn đến kết quả không nhất quán. Tôi đã sai tất cả thời gian đó sao? – zneak

+1

Có nhiều vấn đề khác hơn là đọc/ghi nguyên tử. 2 chủ đề hoạt động trên cùng một biến có thể rất tốt dẫn đến kết quả không mong muốn gây ra họ thường đọc/sửa đổi/ghi mà không phải là nguyên tử. Cũng có những vấn đề về khả năng hiển thị bộ nhớ trên các máy đa xử lý cần được chăm sóc đặc biệt – nos

-4

Bạn không thể. Ngay cả khi đi tất cả các con đường xuống ngôn ngữ lắp ráp, bạn phải sử dụng mã khóa đặc biệt LOCK để đảm bảo rằng một lõi hoặc thậm chí quá trình sẽ không đi xung quanh và quét sạch tất cả các công việc khó khăn của bạn.

+0

Vì vậy, đó là những gì các jitter .net đang làm đằng sau hậu trường? Thêm khóa vào mỗi biến không phải địa phương đọc hoặc ghi? – zneak

+0

Tôi sẽ không yêu cầu biết tất cả các chi tiết. Tôi đang nói từ một quan điểm chung. –

3

Khi đọc và viết x86 vẫn là nguyên tử. Nó được hỗ trợ ở cấp độ phần cứng. Tuy nhiên, điều này không có nghĩa là các phép toán như phép cộng và phép nhân là nguyên tử; họ yêu cầu một tải, tính toán, sau đó lưu trữ, có nghĩa là họ có thể can thiệp. Đó là nơi tiền tố khóa xuất hiện.

Bạn đã đề cập đến rào cản khóa và bộ nhớ; họ không có liên quan gì đến việc đọc và viết là nguyên tử. Không có cách nào trên x86 có hoặc không sử dụng các rào cản bộ nhớ mà bạn sẽ thấy một giá trị 32-bit được viết một nửa.

+1

Vâng, tôi biết phép nhân và bạn bè của họ không phải là nguyên tử. Không bao giờ có nhầm lẫn về điều này về phía tôi. Tôi bằng cách nào đó tin rằng một CPU có thể truy cập bộ nhớ trong khi một CPU khác đang viết ở đó, điều này có thể dẫn đến kết quả không nhất quán về đọc. Nó có thể xảy ra không, và nếu vậy tôi có thể làm điều gì đó để nó không xảy ra? – zneak

+0

Không, nó không thể xảy ra. Tôi đã nói rõ điều đó rồi. – wj32

4

Nó là khá rẻ để thực hiện bảo đảm nguyên tử trên lõi x86 và x64 kể từ khi CLR chỉ hứa hẹn atomicity cho các biến 32-bit hoặc nhỏ hơn. Tất cả những gì được yêu cầu là biến được căn chỉnh chính xác và không nằm trong một đường bộ nhớ cache. Trình biên dịch JIT đảm bảo điều này bằng cách phân bổ các biến cục bộ trên một bù ngăn xếp liên kết 4 byte. Trình quản lý heap GC thực hiện tương tự cho việc phân bổ heap.

Đáng chú ý là bảo đảm CLR không phải là một điều rất tốt. Lời hứa liên kết không đủ tốt để viết mã luôn phù hợp với các mảng tăng gấp đôi. Rất độc đáo được chứng minh trong this thread. Interop với mã máy sử dụng hướng dẫn SIMD cũng rất khó vì lý do này.

+0

Tôi thấy nó tò mò rằng bộ cấp phát bộ nhớ cố gắng giải quyết vấn đề căn chỉnh cho mảng tăng gấp đôi bằng cách buộc chúng vào LOH, thay vì ví dụ: nói rằng bất cứ khi nào GC phân bổ hoặc sao chép một 'double []' của 32 mục hoặc lớn hơn, và không gian có sẵn tiếp theo không phải là 64-bit căn, trước tiên nó phải phân bổ và loại bỏ một đối tượng giả 12 byte. Các bộ nhớ 5% chất thải nên ít chi phí hơn so với chi phí buộc những thứ để LOH. – supercat

2

Có, bảo đảm C# và Java rằng tải và lưu trữ một số loại nguyên thủy là nguyên tử, như bạn nói. Điều này là rẻ bởi vì các bộ vi xử lý có khả năng chạy .NET hoặc JVM đảm bảo rằng tải và các cửa hàng của các kiểu nguyên thủy phù hợp phù hợp là nguyên tử. Bây giờ, những gì không phải là C# hay Java cũng như bộ vi xử lý mà chúng chạy trên bảo đảm, và đắt tiền, đang phát hành các rào cản bộ nhớ để các biến đó có thể được sử dụng để đồng bộ hóa trong một chương trình đa luồng. Tuy nhiên, trong Java và C# bạn có thể đánh dấu biến của bạn với thuộc tính "dễ bay hơi", trong trường hợp đó trình biên dịch sẽ xử lý các rào cản bộ nhớ thích hợp.

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