Lưu ý: Câu trả lời này (bao gồm cả hai lần chỉnh sửa) đã được đưa ra trước khi câu hỏi được thay đổi thành có độ dài count
. Đối với các câu hỏi hiện tại thay vì Thread.VolatileRead
Tôi sẽ sử dụng Interlocked.Read
, mà cũng có ngữ nghĩa dễ bay hơi và cũng sẽ đối phó với vấn đề đọc 64-bit thảo luận ở đây và đưa vào câu hỏi
Một đọc nguyên tử được đảm bảo mà không cần khóa, bởi vì lần đọc của các giá trị được căn chỉnh đúng 32 bit hoặc ít hơn, mà giá trị count
của bạn được đảm bảo là nguyên tử.
Giá trị này khác với giá trị 64 bit nếu nó bắt đầu ở -1 và được đọc trong khi chuỗi khác tăng lên, có thể dẫn đến giá trị là -1 (xảy ra trước khi tăng), 0 (xảy ra sau khi tăng)) hoặc là 4294967295 hoặc -4294967296 (32 bit được ghi bằng 0, 32 bit khác đang chờ ghi).
Tăng nguyên tử Interlocked.Increment
có nghĩa là toàn bộ hoạt động gia tăng là nguyên tử. Hãy xem xét số gia tăng đó là khái niệm:
- Đọc giá trị.
- Thêm một giá trị.
- Viết giá trị.
Sau đó, nếu x
là 54 và một thread cố gắng để tăng nó trong khi cố gắng khác để đặt nó là 67, hai giá trị đúng có thể là 67 (tăng xảy ra trước, sau đó được viết lên) hoặc 68 (phân xảy ra đầu tiên , sau đó được tăng lên) nhưng tăng không nguyên tử có thể dẫn đến 55 (tăng đọc, chuyển nhượng 67 xảy ra, tăng ghi).
Trường hợp thực tế phổ biến hơn là x
là 54 và tăng một luồng và một số lần giảm khác. Ở đây kết quả hợp lệ duy nhất là 54 (một lên, sau đó một xuống, hoặc ngược lại), nhưng nếu không phải nguyên tử thì kết quả có thể là 53, 54 và 55.
Nếu bạn chỉ muốn một số được tăng lên một cách nguyên tử, mã đúng là:
private int count;
public int Count
{
get
{
return Thread.VolatileRead(byref count);
}
}
public void Increment
{
Interlocked.Increment(count);
}
Nếu bạn muốn hành động theo số đó, bạn cần khóa mạnh hơn. Điều này là do các chủ đề bằng cách sử dụng số lượng có thể trở thành lỗi thời trước khi hoạt động của nó kết thúc. Trong trường hợp này, bạn cần phải khóa tất cả mọi thứ mà quan tâm đến số lượng và tất cả mọi thứ thay đổi nó. Chỉ cần làm thế nào điều này cần được thực hiện (và cho dù nó thậm chí quan trọng để làm ở tất cả) phụ thuộc vào nhiều vấn đề của trường hợp sử dụng của bạn hơn có thể được suy ra từ câu hỏi của bạn.
Chỉnh sửa: Ồ, bạn có thể muốn khóa đơn giản để buộc hàng rào bộ nhớ. Bạn cũng có thể thay đổi triển khai thực hiện Count
thành return Thread.VolatileRead(ref count);
để đảm bảo bộ đệm CPU bị xóa nếu bạn định tháo khóa. Nó phụ thuộc vào mức độ nghiêm trọng của cache trong trường hợp này. (Một cách khác là làm cho count
dễ bay hơi, vì vậy tất cả các lần đọc và ghi sẽ dễ bay hơi. Lưu ý rằng điều này không cần thiết cho các hoạt động Interlocked
vì chúng luôn luôn biến động.)
Chỉnh sửa 2: Thật vậy, bạn nên có khả năng muốn đọc điều này dễ bay hơi, rằng tôi đang thay đổi câu trả lời ở trên. Nó là có thể bạn sẽ không quan tâm về những gì nó cung cấp, nhưng ít có khả năng hơn nhiều.
Không bao giờ khóa trên 'this', khóa sẽ xung đột với một bên ngoài khóa lớp trên đối tượng. Chỉ bao giờ khóa các thành viên kiểu tham chiếu riêng tư. Nếu bạn không có một thành viên kiểu tham chiếu riêng tư thích hợp của kiểu đúng kiểu vis-a-vis static hoặc instance thì có 'private object _myLock;' hoặc 'private static object _myStaticLock;' chỉ cho mục đích đó. –
@Jon, đã lưu ý và chỉnh sửa –
Tôi vẫn có cảm giác bạn có thể có cá lớn hơn để chiên hơn bao phủ trong câu hỏi này. Tôi hài lòng với câu trả lời của tôi cho đến khi câu hỏi này xảy ra, nhưng nó không giải quyết các vấn đề của luồng A đang làm gì đó trên cơ sở đếm trong khi chuỗi B thay đổi số lượng, bởi vì đó có phải là vấn đề hay không và nếu nó là, phụ thuộc vào nhiều hơn bạn đưa ra ở đây. –