31

C# 4 in a Nutshell (rất khuyến khích btw) sử dụng đoạn mã sau để chứng minh các khái niệm về MemoryBarrier (giả sử A và B đã được chạy trên chủ đề khác nhau):Tại sao tôi cần một rào cản bộ nhớ?

class Foo{ 
    int _answer; 
    bool complete; 
    void A(){ 
    _answer = 123; 
    Thread.MemoryBarrier(); // Barrier 1 
    _complete = true; 
    Thread.MemoryBarrier(); // Barrier 2 
    } 
    void B(){ 
    Thread.MemoryBarrier(); // Barrier 3; 
    if(_complete){ 
     Thread.MemoryBarrier(); // Barrier 4; 
     Console.WriteLine(_answer); 
    } 
    } 
} 

họ đề cập rằng rào cản 1 & 4 ngăn chặn điều này ví dụ từ viết 0 và hàng rào 2 & 3 cung cấp bảo đảm độ tươi: chúng đảm bảo rằng nếu B chạy sau A, hãy đọc _complete sẽ đánh giá đúng.

Tôi không thực sự nhận được nó. Tôi nghĩ rằng tôi hiểu tại sao Rào cản 1 & 4 là cần thiết: chúng ta không muốn ghi vào _answer được tối ưu hóa và được đặt sau khi ghi vào _complete (Barrier 1) và chúng ta cần phải đảm bảo rằng _answer là không được lưu trữ (Rào cản 4). Tôi cũng nghĩ rằng tôi hiểu tại sao Barrier 3 là cần thiết: nếu A chạy cho đến khi chỉ sau khi viết _complete = true, B vẫn cần phải làm mới _complete để đọc đúng giá trị.

Tôi không hiểu tại sao chúng ta cần Barrier 2! Một phần của tôi nói rằng đó là bởi vì có lẽ Chủ đề 2 (chạy B) đã chạy đến (nhưng không bao gồm) nếu (_complete) và vì vậy chúng tôi cần đảm bảo rằng _complete được làm mới.

Tuy nhiên, tôi không thấy điều này sẽ hữu ích như thế nào. Không phải vẫn có thể là _complete sẽ được đặt thành true trong A nhưng phương pháp B sẽ thấy phiên bản được lưu trong bộ nhớ cache (false) là _complete? Tức là, nếu Thread 2 chạy phương thức B cho đến sau MemoryBarrier đầu tiên và sau đó Thread 1 chạy phương thức A cho đến _complete = true nhưng không tiếp tục, và sau đó Thread 1 tiếp tục và thử nghiệm nếu (_complete) - có thể nếu không dẫn đến false?

+0

Tại sao mọi người sử dụng tính năng này trên 'biến động'? – ChaosPandion

+6

@Chaos: CLR qua C# book (Richter) có một lời giải thích tuyệt vời - IIRC nó 'dễ bay hơi' có nghĩa là tất cả các truy cập vào var được coi là dễ bay hơi và thực thi các rào cản bộ nhớ đầy đủ theo cả hai hướng. Đó là cách thường xuyên hơn perf hit hơn cần thiết nếu bạn thay vì chỉ cần một đọc hoặc viết một rào cản và chỉ trong truy cập cụ thể. –

+1

@Chaos: không thực sự là điểm, nhưng một lý do là dễ bay hơi có những quirks riêng của nó liên quan đến tối ưu hóa trình biên dịch có thể dẫn đến bế tắc, xem http://www.bluebytesoftware.com/blog/2009/02/24/TheMagicalDuelingDeadlockingSpinLocks .aspx – hackerhasid

Trả lời

24

Rào cản # 2 người được ủy quyền viết thư đến _complete được cam kết ngay lập tức. Nếu không, nó có thể vẫn ở trạng thái xếp hàng có nghĩa là số đọc của _complete trong B sẽ không thấy thay đổi gây ra bởi A mặc dù B đã sử dụng hiệu quả đọc dễ bay hơi.

Tất nhiên, ví dụ này không hoàn toàn làm công lý cho vấn đề bởi vì A không làm gì hơn sau khi viết tới _complete có nghĩa là ghi sẽ được gửi ngay lập tức kể từ khi chuỗi kết thúc sớm.

Câu trả lời cho câu hỏi của bạn về việc liệu if vẫn có thể đánh giá là false là có cho chính xác lý do bạn đã nêu. Nhưng, hãy chú ý những gì tác giả nói về điểm này.

Rào cản 1 và 4 ngăn chặn ví dụ này từ viết “0”. Rào cản 2 và 3 cung cấp sự đảm bảo về độ tươi: chúng đảm bảo rằng nếu B chạy sau A, đọc _hoàn thành sẽ được đánh giá là đúng.

Sự nhấn mạnh vào "nếu B chạy sau A" là của tôi. Nó chắc chắn có thể là trường hợp mà hai chủ đề xen kẽ. Tuy nhiên, tác giả đã bỏ qua kịch bản này có lẽ là để làm cho quan điểm của mình liên quan đến cách Thread.MemoryBarrier hoạt động đơn giản như thế nào.

Nhân tiện, tôi đã gặp khó khăn khi đưa ra một ví dụ trên máy của tôi, nơi các rào cản # 1 và # 2 sẽ thay đổi hành vi của chương trình. Điều này là do mô hình bộ nhớ liên quan đến viết đã mạnh mẽ trong môi trường của tôi. Có lẽ, nếu tôi có một máy đa xử lý, đang sử dụng Mono, hoặc có một số thiết lập khác nhau khác tôi có thể đã chứng minh nó. Tất nhiên, thật dễ dàng để chứng minh rằng việc loại bỏ các rào cản # 3 và # 4 có tác động.

+0

Cảm ơn bạn, điều đó rất hữu ích. Tôi đoán tôi không phải là không biết gì khi tôi nghĩ. – hackerhasid

+0

Tôi không hiểu cả hai rào cản 2 * và * 3 là cần thiết trong trường hợp B chạy sau A. Cả hai đều là hàng rào đầy đủ, vì vậy bất kỳ ai trong số họ sẽ làm một mình, phải không? –

+5

@ohadsc: Các rào cản bộ nhớ ảnh hưởng đến hành vi của một chuỗi duy nhất. Hãy xem xét rằng A và B có thể chạy trên các CPU khác nhau. Nếu bạn loại bỏ rào cản 2 thì ghi có thể không được cam kết. Nếu bạn loại bỏ rào cản 3 thì có thể không đọc được phần đọc. Các rào cản trong A không ảnh hưởng đến việc thi hành B và ngược lại. –

0

Các ví dụ là không rõ ràng vì hai lý do:

  1. Đó là quá đơn giản để hiển thị đầy đủ những gì đang xảy ra với hàng rào.
  2. Albahari bao gồm các yêu cầu đối với kiến ​​trúc không phải x86. Xem MSDN: "MemoryBarrier chỉ được yêu cầu trên các hệ thống đa bộ xử lý có thứ tự bộ nhớ yếu (ví dụ, một hệ thống sử dụng nhiều bộ xử lý Intel Itanium [mà Microsoft không còn hỗ trợ]).".

Nếu bạn xem xét những điều sau đây, nó trở nên rõ ràng hơn:

  1. Một rào cản bộ nhớ (đầy đủ các hàng rào ở đây - Net không cung cấp một rào cản rưỡi) ngăn đọc hướng dẫn/ghi từ nhảy hàng rào (do tối ưu hóa khác nhau). Điều này đảm bảo cho chúng tôi mã sau khi hàng rào sẽ thực thi sau mã trước hàng rào.
  2. "Thao tác tuần tự hóa này đảm bảo rằng mọi hướng dẫn tải và lưu trữ đứng trước thứ tự chương trình lệnh MFENCE được hiển thị trên toàn cầu trước khi bất kỳ lệnh tải hoặc lưu trữ nào tuân theo lệnh MFENCE được hiển thị trên toàn cầu." Xem here.
  3. x86 CPU có mô hình bộ nhớ mạnh và đảm bảo ghi xuất hiện phù hợp với tất cả các luồng/lõi (do đó rào cản # 2 & # 3 không cần thiết trên x86). Tuy nhiên, chúng tôi không đảm bảo rằng việc đọc và viết sẽ vẫn còn trong chuỗi được mã hóa, do đó cần phải có các rào cản # 1 và # 4.
  4. Rào cản bộ nhớ không hiệu quả và không cần sử dụng (xem cùng bài viết MSDN). Cá nhân tôi sử dụng Interlocked và dễ bay hơi (đảm bảo bạn biết cách sử dụng nó một cách chính xác !!), hoạt động hiệu quả và dễ hiểu.

Ps. This article giải thích các hoạt động bên trong của x86 độc đáo.

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