2013-07-26 48 views
5

Tôi có nhiều luồng truy cập một biến Int32 đơn với thao tác "++" hoặc "-".Là hoạt động của nguyên tử trong C#?

Chúng ta có cần khóa trước khi truy cập nó như dưới đây không?

lock (idleAgentsLock) 
    { 
     idleAgents--; //we should place it here. 
    } 

Hoặc tôi sẽ không có hậu quả gì nếu khóa?

+1

Câu hỏi hay hơn là khả năng, là gì * sử dụng * 'idleAgents' đồng thời an toàn? – user2246674

+0

có thể trùng lặp của [Nguyên tắc toán tử ++ có an toàn không?] (Http://stackoverflow.com/questions/4628243/is-the-operator-thread-safe) – nawfal

Trả lời

6

Nó không phải là "nguyên tử", không theo ý nghĩa đa luồng. Tuy nhiên, khóa của bạn bảo vệ hoạt động, về mặt lý thuyết ít nhất. Khi nghi ngờ bạn tất nhiên có thể sử dụng Interlocked.Decrement để thay thế.

5

Không ++/- không phải là hoạt động nguyên tử, tuy nhiên việc đọc và ghi vào số nguyên và thời gian nguyên thủy khác được coi là hoạt động nguyên tử.

Xem những liên kết này:

Cũng blog post cách này có một số quan tâm đến bạn.

Tôi sẽ đề xuất Interlocked.Increment trong lớp Interlocked để thực hiện nguyên tử các toán tử ++/-.

1

Nó phụ thuộc vào kiến ​​trúc máy. Nói chung, không, trình biên dịch có thể tạo ra một lệnh tải, tăng/giảm giá trị và sau đó lưu trữ nó. Vì vậy, các chủ đề khác thực sự có thể đọc giá trị giữa các hoạt động đó.

Hầu hết các bộ chỉ lệnh CPU đều có một thử nghiệm nguyên tử đặc biệt & đặt lệnh cho mục đích này. Giả sử bạn không muốn nhúng hướng dẫn lắp ráp vào mã C# của bạn, cách tiếp cận tốt nhất tiếp theo là sử dụng loại trừ lẫn nhau, tương tự như những gì bạn đã hiển thị. Việc thực hiện cơ chế đó cuối cùng sử dụng một lệnh mà là nguyên tử để thực hiện mutex (hoặc bất cứ điều gì nó sử dụng).

Tóm lại: có, bạn nên đảm bảo loại trừ lẫn nhau.

Ngoài phạm vi của câu trả lời này, còn có các kỹ thuật khác để quản lý dữ liệu được chia sẻ có thể phù hợp hoặc không phụ thuộc vào logic miền của tình huống của bạn.

1

Tăng/giảm không được bảo vệ không an toàn theo chủ đề - và do đó không phải là nguyên tử giữa các chuỗi. (Mặc dù nó có thể là "nguyên tử" WRT IL thực tế/ML chuyển .)

LINQPad mã ví dụ này cho thấy kết quả không thể đoán trước:

void Main() 
{ 
    int nWorkers = 10; 
    int nLoad = 200000; 
    int counter = nWorkers * nLoad; 
    List<Thread> threads = new List<Thread>(); 
    for (var i = 0; i < nWorkers; i++) { 
     var th = new Thread((_) => { 
      for (var j = 0; j < nLoad; j++) { 
       counter--; // bad 
      } 
     }); 
     th.Start(); 
     threads.Add(th); 
    } 
    foreach (var w in threads) { 
     w.Join(); 
    } 
    counter.Dump(); 
} 

Lưu ý rằng khả năng hiển thị giữa các chủ đề có tầm quan trọng . Đồng bộ hóa đảm bảo khả năng hiển thị này ngoài nguyên tử.

Mã này dễ dàng được sửa, ít nhất trong ngữ cảnh giới hạn được trình bày.Chuyển ra sụt lần và quan sát kết quả:

counter--;       // bad 
Interlocked.Decrement(ref counter); // good 
lock (threads) { counter--; }  // good 

Ngay cả khi sử dụng một biến volatile, kết quả là vẫn không thể đoán trước. Điều này dường như chỉ ra rằng (ít nhất ở đây, khi tôi chỉ chạy nó) nó cũng là không một nhà điều hành nguyên tử như đọc/op/ghi của các chủ đề cạnh tranh được xen kẽ. Để thấy rằng hành vi này là vẫn không chính xác khi các vấn đề tầm nhìn được loại bỏ (chúng là gì?), Thêm

class x { 
    public static volatile int counter; 
} 

và sửa đổi các mã trên sử dụng x.counter thay vì biến counter địa phương.

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