2010-11-24 48 views
9

Để thực hiện một khóa miễn phí Mã cho đa luồng ứng dụng tôi đã sử dụng volatile biến, Về mặt lý thuyết: Từ khoá volatile chỉ đơn giản được sử dụng để đảm bảo rằng tất cả các chủ đề nhìn thấy giá trị được cập nhật nhất của một biến biến động; do đó, nếu thread A cập nhật giá trị biến và chuỗi B đọc biến đó ngay sau khi cập nhật đó xảy ra, nó sẽ thấy giá trị được cập nhật gần đây nhất được viết từ chủ đề A. Khi tôi đọc trong một số C# 4.0 trong Nutshell book đây là saidễ bay hơi và Thread.MemoryBarrier trong C#

áp dụng ổn định không ngăn cản một ghi theo sau là một đọc khỏi bị hoán đổi.

thể vấn đề này được giải quyết bằng cách đặt Thread.MemoryBarrier() trước mỗi get của biến volatile như:

private volatile bool _foo = false; 

private void A() 
{ 
    //… 
    Thread.MemoryBarrier(); 
    if (_foo) 
    { 
     //do somthing 
    } 
} 

private void B() 
{ 
    //… 
    _foo = true; 
    //… 
} 

Và nếu điều này giải quyết vấn đề; xem xét chúng ta có một vòng lặp while phụ thuộc vào giá trị đó tại một trong các điều kiện của nó; đang đặt Thread.MemoryBarrier() trước vòng lặp while là một cách chính xác để khắc phục sự cố? ví dụ:

private void A() 
{ 
    Thread.MemoryBarrier(); 
    while (_someOtherConditions && _foo) 
    { 
     // do somthing. 
    } 
} 

Để chính xác hơn, tôi muốn biến số _foo mang lại giá trị mới nhất khi bất kỳ chuỗi nào yêu cầu bất kỳ lúc nào; vì vậy nếu chèn Thread.MemoryBarrier() trước khi gọi biến sửa chữa vấn đề sau đó tôi có thể sử dụng Foo tài sản thay vì _foo và làm một Thread.MemoryBarrier() trong get của rằng bất động sản như:

Foo 
{ 
    get 
    { 
     Thread.MemoryBarrier(); 
     return _foo; 
    } 
    set 
    { 
     _foo = value; 
    } 
} 
+0

Có thể trùng lặp ... http: //stackoverflow.com/questions/1330590/when-to-use-volatile-or-thread-memorybarrier-in-threadsafe-locking-code-c –

+0

@Aron; không có nó không trùng lặp; đây là một câu hỏi khác. –

+0

@Jalal Bạn đang cố gắng giải quyết một vấn đề bằng cách sử dụng phương pháp này dễ bay hơi/memorybarrier hoặc bạn ok với bất kỳ cách tiếp cận nào giải quyết vấn đề của bạn? –

Trả lời

8

"C# Tóm tắt" là chính xác, nhưng tuyên bố của nó là tranh luận. Tại sao?

  • A 'ghi' theo sau là một 'đọc', withot 'bay hơi', đảm bảo sẽ xảy ra trong chương trình đặt hàng nào nếu nó ảnh hưởng Logic trong một chủ đề duy nhất
  • Các 'ghi' trước 'đọc' trong chương trình đa luồng là hoàn toàn vô nghĩa để lo lắng về ví dụ của bạn.

Hãy làm rõ. Lấy mã ban đầu của bạn:

private void A() 
{ 
    //… 
    if (_foo) 
    { 
     //do something 
    } 
} 

gì xảy ra nếu scheduler chủ đề đã kiểm tra các biến _foo, nhưng nó được treo ngay trước //do something bình luận? Vâng, tại thời điểm đó thread khác của bạn có thể thay đổi giá trị của _foo, có nghĩa là tất cả các volatiles của bạn và Thread.MemoryBarriers tính cho không có gì !!! Nếu bạn cần tránh do_something nếu giá trị _foo là sai, thì bạn không còn cách nào khác ngoài việc sử dụng khóa.

Tuy nhiên, nếu được phép do something thực thi khi đột ngột _foo trở thành sai, thì điều đó có nghĩa là từ khóa dễ bay hơi là quá đủ cho nhu cầu của bạn.

Để rõ ràng: tất cả những người phản hồi đang yêu cầu bạn sử dụng hàng rào bộ nhớ không chính xác hoặc đang cung cấp quá mức cần thiết.

+0

cảm ơn bạn đã trả lời; Trong trường hợp của tôi, tôi muốn tránh do_something được thực hiện nếu _foo là đúng càng nhiều càng tốt, nhưng nó là ** không phải là chết người quan trọng **; nhưng nếu tôi có thể làm cho nó chính xác hơn với MemoryBarrier thì ** tại sao không ** sử dụng nó với biến động .. Nếu lịch trình chuỗi treo chuỗi sau khi kiểm tra _foo "phù thủy có thể" thì do_somthing sẽ thực thi nhưng ít nhất chúng tôi ** giảm cơ hội ** rằng _foo sẽ là sai tại thời điểm đó phải không? –

+2

@Jalal: khai báo "volatile' đã được người bảo đảm, tất cả bởi chính nó, rằng biến sẽ không được lưu trữ trong thanh ghi CPU. Nó gaurantees trực tiếp bộ nhớ đọc và viết (mà làm mất hiệu lực bộ nhớ cache của bộ vi xử lý khác trên một máy đa xử lý). Do đó rào cản bộ nhớ rõ ràng là không cần thiết; 'volatile' đã hoàn thành những gì bạn cần. –

+0

@BrentArias MemoryBarrier * là * cần thiết vì bộ vi xử lý có thể cập nhật giá trị trên một bộ xử lý chứ không phải trên bộ xử lý khác vì vậy một luồng đọc giá trị cập nhật và luồng khác đọc giá trị cũ. – Anthony

0

Trong ví dụ thứ hai của bạn, bạn sẽ cũng cần phải đặt một Thread.MemoryBarrier(); bên trong vòng lặp, để đảm bảo bạn nhận được giá trị gần đây nhất mỗi khi bạn kiểm tra điều kiện vòng lặp.

+0

nếu điều này cố định vấn đề sau đó đặt Thread.MemoryBarrier() nên ở dòng cuối cùng trước khi đóng khung của vòng lặp while và trước cuộc gọi của vòng lặp while .. –

+0

@Jalal: Tôi khá chắc chắn rằng 'MemoryBarrier rõ ràng 'các cuộc gọi không được yêu cầu trong các ví dụ này, miễn là' _foo' được đánh dấu là 'volatile'. (Tôi không có chuyên gia, tôi có thể chỉ sử dụng một 'khóa'.) – LukeH

+2

Trong trường hợp này: việc sử dụng Thread.MemoryBarrier, cho dù trước, sau, hoặc cả hai, không đạt được bất cứ điều gì mà dễ bay hơi chưa đạt được . –

0

Kéo từ here ...

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); 
    } 
    } 
} 

rào 1 và 4 ngă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.

Vì vậy, nếu chúng ta quay trở lại ví dụ vòng lặp của bạn ... Đây là cách nó nên nhìn ...

lời của
private void A() 
{ 
    Thread.MemoryBarrier(); 
    while (_someOtherConditions && _foo) 
    { 
     //do somthing 
     Thread.MemoryBarrier(); 
    } 
} 
+1

@ Aaron Cảm ơn tôi kiểm tra liên kết trước khi nó ở cùng một cuốn sách "C# 4.0 trong một Nutshell" .. Tuy nhiên nếu điều này cố định vấn đề sau đó đặt Thread.MemoryBarrier() nên ở dòng cuối cùng trước khi đóng khung của vòng lặp while . –

+0

@Jalal Nó phụ thuộc vào việc bạn làm gì đó, nơi mà ThreadBarrier hiện đang ảnh hưởng đến _someOtherconditions/_foo vars –

+1

@Aaron: Nếu '_foo' được đánh dấu là' volatile' thì tôi khá chắc chắn rằng 'MemoryBarrier' rõ ràng các cuộc gọi là không cần thiết trong các ví dụ cụ thể này. (Khá chắc chắn nhưng không chắc chắn 100%, và nếu nghi ngờ thì tôi có lẽ chỉ cần sử dụng một 'khóa'.) – LukeH

0

của Microsoft về hàng rào bộ nhớ:

MemoryBarrier được yêu cầu chỉ trên 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).

Với hầu hết các mục đích, câu lệnh khóa C#, câu lệnh Visual Basic SyncLock hoặc lớp Monitor cung cấp các cách dễ dàng hơn để đồng bộ hóa dữ liệu.

+0

Vui lòng tham khảo http://www.albahari.com/threading/part4.aspx để thấy rằng các từ của Microsoft không chính xác !!. –

+0

Áp phích đặc biệt yêu cầu thực hiện không có khóa. –

5

Cuốn sách là chính xác.
Mô hình bộ nhớ của CLR cho biết rằng các hoạt động tải và lưu trữ có thể được sắp xếp lại. Điều này xảy ra cho biến số không ổn định không biến động.

Khai báo một biến như volatile chỉ có nghĩa là hoạt động tải sẽ có Acquire ngữ nghĩa, và các hoạt động cửa hàng sẽ có phát hành ngữ nghĩa. Ngoài ra, trình biên dịch sẽ tránh thực hiện các tối ưu hóa nhất định mà chuyển tiếp trên thực tế là biến được truy cập theo kiểu chuỗi đơn, được tuần tự hóa (ví dụ: tải trọng/lưu trữ trong vòng lặp).

Chỉ sử dụng volatile từ khóa không tạo ra các phần quan trọng và không khiến cho các chuỗi đồng bộ hóa một cách kỳ diệu với nhau.

Bạn phải cực kỳ cẩn thận khi viết mã miễn phí khóa. Không có gì đơn giản về nó, và ngay cả các chuyên gia cũng gặp khó khăn để làm đúng.
Dù là vấn đề ban đầu bạn đang cố gắng giải quyết, có khả năng là có cách hợp lý hơn để thực hiện điều đó.

+0

@Liran: Tôi biết rằng việc sử dụng biến động một mình không tạo ra các phần quan trọng và cũng không gây ra chuỗi đồng bộ với nhau. Tôi chỉ yêu cầu biến _foo phải cập nhật giá trị khi có bất kỳ chuỗi nào yêu cầu nó bất kỳ lúc nào trong mã khóa miễn phí. –

+0

@Jalal, tuyên bố biến là dễ bay hơi sẽ đảm bảo rằng mọi hoạt động tải sẽ đọc giá trị cập nhật nhất của biến (ví dụ: từ bộ nhớ chính thay vì đăng ký). Bạn không nên lây lan các rào cản bộ nhớ trên tất cả các mã của bạn ... làm như vậy có thể có một tác động tiêu cực lớn đến hiệu suất. Một lần nữa, nếu bạn không chỉ cố gắng để chơi xung quanh và thử nghiệm với mã khóa miễn phí, và có thật, mã sản xuất trong hình ... Tôi khuyến khích bạn tìm một giải pháp khác. – Liran

+0

@Liran; "tuyên bố biến là dễ bay hơi sẽ đảm bảo rằng mọi hoạt động tải sẽ đọc giá trị cập nhật nhất của biến"; sẽ như tôi hiểu từ cuốn sách http://www.albahari.com/threading/part4.aspx mà wirte follwed bằng cách đọc có thể không nhận được giá trị cập nhật nhất, và có tôi đang cố gắng để thử nghiệm với khóa mã miễn phí; câu hỏi đặt ra là: đó có phải là một tác động tiêu cực lớn đến hiệu suất của ứng dụng khi thực hiện 'Thread.MemoryBarrier' trước khi có bất kỳ hoạt động nào của biến dễ bay hơi, nếu không tôi có thể đặt nó vào một thuộc tính trước khi nhận được biến.vui lòng xem câu hỏi cập nhật –

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