2011-08-03 29 views
12

Được biết, tham chiếu mất 4 Byte bộ nhớ trong bộ xử lý 32 bit và 8 Byte - trong bộ xử lý 64 bit. Vì vậy, bộ vi xử lý đảm bảo rằng duy nhất đọc từ và ghi vào bộ nhớ trong gia số của kích thước từ tự nhiên của máy sẽ được thực hiện nguyên tử. Mặt khác có 2 phương pháp trong lớp đan cài:Sử dụng Interlocked.Exchange để cập nhật tài liệu tham khảo và Int32

public static int Exchange(
    ref int location1, 
    int value 
) 

public static T Exchange<T>(
    ref T location1, 
    T value 
) 
where T : class 

Vì vậy, câu hỏi là tại sao Interlocked.Exchange là cần thiết cho Int32 và với nhiều loại tài liệu tham khảo? Nó có thể được thực hiện một cách an toàn bằng cách sử dụng nhiệm vụ đơn giản vì nó là nguyên tử?

Trả lời

9

Nó không chỉ là về nguyên tử. Nó cũng là về khả năng hiển thị bộ nhớ. Biến có thể được lưu trữ trong bộ nhớ chính hoặc trong bộ nhớ cache CPU. Nếu biến chỉ được lưu trữ trong bộ nhớ cache CPU, nó sẽ không được hiển thị cho các luồng chạy trên CPU khác nhau. Hãy xem xét ví dụ sau:

public class Test { 
    private Int32 i = 5; 

    public void ChangeUsingAssignment() { 
     i = 10; 
    } 

    public void ChangeUsingInterlocked() { 
     Interlocked.Exchange(ref i, 10); 
    } 

    public Int32 Read() { 
     return Interlocked.CompareExchange(ref i, 0, 0); 
    } 
} 

Bây giờ nếu bạn gọi 'ChangeUsingAssignment' trên một thread và 'Đọc' trên thread khác giá trị trả về có thể là 5, không phải 10. Nhưng nếu bạn gọi ChangeUsingInterlocked, 'Đọc' sẽ trả lại 10 như mong đợi.

----------   ------------   ------------------- 
| CPU 1 | --> | CACHE 1 | --> |     | 
----------   ------------  |     | 
             |  RAM  | 
----------   ------------  |     | 
| CPU 2 | --> | CACHE 2 | --> |     | 
----------   ------------   ------------------- 

Trong biểu đồ ở trên phương pháp 'ChangeUsingAssignement' có thể dẫn đến giá trị 10 bị 'kẹt' trong CACHE 2 và không chuyển sang RAM. Khi CPU 1 sau đó cố gắng đọc nó, nó sẽ nhận được giá trị từ RAM khi nó vẫn còn 5. Sử dụng Interlocked thay vì ghi bình thường sẽ đảm bảo rằng giá trị 10 được tất cả các cách để RAM.

+0

Cảm ơn bạn rất nhiều. Bây giờ đã đủ rõ ràng rồi. –

+3

Tôi biết điều này rõ ràng là một năm sau đó, nhưng nếu có thể, bạn có thể vui lòng xem lại điều này? Trang web này http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/ dường như ngụ ý rằng tất cả các ghi trong C# đã biến động. – user981225

5

Trao đổi giá trị bộ nhớ và nội dung của thanh ghi CPU nói chung không phải là nguyên tử. Cả hai bạn cần phải đọc và viết vị trí bộ nhớ. Hơn nữa, các phương thức Interlocked đảm bảo rằng hoạt động này là nguyên tử ngay cả trên các máy tính đa lõi trong đó mỗi lõi có bộ nhớ cache riêng và có khả năng xem riêng bộ nhớ chính của nó.

+0

Nhưng nếu tôi không cần phải có giá trị cũ và chỉ muốn chỉ định giá trị mới cho một số biến. Đây chỉ là viết và nguyên tử, đúng không? –

+0

@OlegDudnyk Tôi nghĩ rằng nó không chính xác, ngay cả khi bạn không quan tâm đến giá trị ban đầu. Xem xét làm thế nào để đạt được điều đó trong ngôn ngữ lắp ráp, phụ thuộc vào nơi src và biến số/giá trị đích của bạn, chúng có thể được dịch thành nhiều hơn một hướng dẫn, giữa 2 hướng dẫn ứng dụng của bạn vẫn có thể bị gián đoạn. Trên một số kiến ​​trúc, bạn có thể khóa bus để đảm bảo không ai có thể đọc bộ nhớ ghi (ví dụ: IA có tiền tố LOCK) và một số hướng dẫn được đảm bảo là nguyên tử (ví dụ: XCHG, CMPXCHG, v.v. trên IA). – codewarrior

8

Interlocked.Exchange có giá trị trả về, cho phép bạn biết giá trị bạn vừa thay thế. Đó là sự kết hợp của việc thiết lập một giá trị mới có được giá trị cũ mà các phương pháp này đạt được.

+0

Vì vậy, nếu bạn không cần giá trị cũ, bạn có thể chỉ cần gán, nhưng sau đó bạn cũng cần một cuộc gọi Barrier để làm mất hiệu lực bộ nhớ cache (s). –

+0

@Henk: Bạn có thể vui lòng cung cấp thêm thông tin chi tiết về cuộc gọi Barrier của bạn không? –

+0

Tài liệu tham khảo cổ điển: http: //www.albahari.com/threading/ –

4

Interlock.Exchange trả lại giá trị gốc khi thực hiện thao tác nguyên tử. Toàn bộ vấn đề là cung cấp một cơ chế khóa. Vì vậy, nó thực sự là hai hoạt động: đọc giá trị ban đầu đặt giá trị mới. Hai cái đó không phải là nguyên tử.

+1

Nhưng nếu tôi không cần phải có giá trị cũ và chỉ muốn chỉ định giá trị mới cho một số biến. Đây chỉ là viết và nguyên tử, đúng không? –

+0

@gorik: Nếu đó là thao tác đọc hoặc ghi trên biến của bất kỳ loại tham chiếu nào hoặc bất kỳ loại giá trị tích hợp nào chiếm bốn byte trở xuống (đối với máy 32 bit) thì có, nó được đảm bảo là nguyên tử . Kiểm tra loạt bài đăng này của Eric Lippert về chủ đề này, họ sẽ có một số trợ giúp: [Nguyên tử, biến động và bất biến khác nhau, phần một] (http://blogs.msdn.com/b/ericlippert/archive/2011/ 05/26/atomicity-volatility-and-immutability-is-different-part-one.aspx) – InBetween

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