2010-03-28 13 views
8

Tôi đã thực hiện đa luồng đơn giản trong VB.NET trong một thời gian và vừa mới nhận được dự án đa luồng lớn đầu tiên của mình. Tôi đã luôn luôn làm tất cả mọi thứ bằng cách sử dụng tuyên bố Synclock bởi vì tôi không nghĩ rằng có một cách tốt hơn.Tại sao nên sử dụng SyncLocks trong .NET cho các hoạt động đơn giản khi lớp có khóa liên kết có sẵn?

Tôi chỉ biết về Interlocked Class - nó làm cho nó trông như thể tất cả điều này:

Private SomeInt as Integer 
Private SomeInt_LockObject as New Object 

Public Sub IntrementSomeInt 
    Synclock SomeInt_LockObject 
     SomeInt += 1 
    End Synclock 
End Sub 

có thể được thay thế bằng một tuyên bố duy nhất:

Interlocked.Increment(SomeInt) 

này xử lý tất cả các khóa trong nội bộ và sửa đổi số. Điều này sẽ đơn giản hơn nhiều so với việc viết khóa của riêng tôi cho các thao tác đơn giản (các hoạt động dài hơn hoặc phức tạp hơn rõ ràng vẫn cần khóa riêng của chúng).

Có lý do tại sao tôi sẽ tự khóa khóa của mình, sử dụng các đối tượng khóa chuyên dụng, khi tôi có thể thực hiện điều tương tự bằng cách sử dụng các phương thức Interlocked?

Trả lời

8

Bạn chính xác; Interlocked nên được sử dụng ở đây và sẽ nhanh hơn SyncLock.
Tuy nhiên, lớp Interlocked không nổi tiếng.

Tuy nhiên, có những tình huống mà bạn cần sử dụng SyncLockInterlocked sẽ không giúp đỡ.

+0

Bạn có thể cung cấp ví dụ về tình huống như vậy không? Nếu tôi chỉ làm một đơn giản tăng/giảm (hoặc. Thêm nếu tôi cần phải thay đổi giá trị nhiều hơn 1), nó có vẻ như Interlocked sẽ là tốt hơn trong mọi trường hợp. Nếu tôi cần một cái gì đó cao cấp hơn (như một quá trình 4 bước mà tôi cần phải đảm bảo đó là chủ đề duy nhất chạy nó tại bất kỳ thời điểm nào), tôi cần một số Đồng bộ hóa. Tôi quan tâm đến một tình huống mà Interlocked sẽ xuất hiện là thích hợp, nhưng là một lựa chọn tồi. – SqlRyan

+3

Nếu bạn đang làm việc với hai biến khác nhau, 'Interlocked' là không đủ. – SLaks

4

Điều này là do không ai biết về điều đó. Thông báo!

+1

+1 Câu trả lời hay. – SLaks

+0

Đó là những gì tôi nghĩ - Tôi thấy rằng nó đã được khoảng từ 1.0, và vì vậy tôi đã kinh ngạc khi tôi chỉ tìm thấy nó. Tôi nghĩ rằng "Có một cái gì đó khác đang xảy ra ở đây mà tôi không nhìn thấy, hoặc người nào khác phương pháp khóa này sẽ phổ biến hơn nhiều".Tôi thực sự chỉ muốn chắc chắn rằng nó là tuyệt vời là nó xuất hiện để được trên cái nhìn đầu tiên. – SqlRyan

+1

Câu trả lời này không truyền đạt đủ thông tin, cũng không trực tiếp trả lời câu hỏi. – Kir

3

Câu trả lời ngắn gọn là sử dụng khóa Monitor (SyncLock trong VB và lock { } trong C#) không chỉ đảm bảo rằng chỉ có một luồng tại một thời điểm có thể truy cập biến (hoặc, theo nghĩa hẹp, chỉ có một luồng tại một thời điểm) có thể có được một khóa trên đối tượng khóa), nhưng nó cũng tạo ra rào cản bộ nhớ cần thiết để đảm bảo rằng lần đọc trên biến không được tối ưu hóa đi.

Nếu bạn không bao giờ chỉ đọc giá trị của biến (nói cách khác, tất cả công việc của bạn được thực hiện thông qua các cuộc gọi đến Interlocked), thì bạn sẽ không sao. Tuy nhiên, nếu bạn cần để có thể thực hiện một đọc bình thường của biến sau đó tình hình phức tạp hơn. Đọc/ghi không khóa thường được thực hiện trong C# bằng cách sử dụng từ khóa volatile. Điều này hướng dẫn trình biên dịch đọc giá trị của biến ở mọi nơi nó được sử dụng, thay vì tối ưu hóa bất kỳ biến nào trong số này đọc vào bộ đệm cục bộ. Thật không may là không có tương đương trong VB.NET, vì vậy bạn sẽ phải sử dụng cái gì khác.

Câu trả lời được chấp nhận cho this question sẽ cung cấp thêm một số thông tin về những gì bạn có thể làm. Trong ngắn hạn, hầu hết mọi người sử dụng SyncLock trong VB.NET vì nó dễ dàng hơn và ít phức tạp hơn so với logic cần thiết để làm điều đó mà không cầnSyncLock.

+0

Không hoàn toàn chính xác, khi bạn thay đổi các biến của mình bằng Interlocked, bạn có thể đọc chúng một cách an toàn mà không cần bất kỳ biện pháp đặc biệt nào. Bởi vì rào chắn là xung quanh các bài viết, các lần đọc được an toàn. –

+0

@Henk: Không thể có nhiều lần đọc tiếp theo lệnh gọi 'Lồng ghép' được tối ưu hóa thành một lần đọc được lưu trong bộ nhớ cache duy nhất? –

+0

Adam, vâng họ có thể. Và từ một quan điểm thời gian thực có vẻ 'sai' nhưng nó sẽ không dẫn đến hành vi không đúng. Kiểm tra các thành viên được khóa. Chỉ có giá trị Đọc cho các bit 64 bit (trên các hệ thống 32 bit). http://msdn.microsoft.com/en-us/library/system.threading.interlocked_members.aspx –

1

Interlocked bị giới hạn đối với các phép toán đơn giản trên Integer, Long và Boolean và như vậy.

Nếu bạn muốn thêm một mục vào Danh sách dùng chung (của T) chẳng hạn, bạn vẫn cần SynClock.

+0

Thật vậy - đó là bởi vì nó là một hoạt động nguyên tử được thực hiện ở cấp phần cứng, tức là CPU sẽ đảm bảo rằng gia số không thể được tranh chấp do đó nó cần ánh xạ tới các nguyên thủy CPU như ints, longs etc – zebrabox

2

Tôi đã từng đọc một lời giải thích rất tốt về các hoạt động phi nguyên tử và nguyên tử (trong VB: lồng vào nhau) và sẽ cố tổng hợp lại.

Normal "non-atomic" operations consist of several steps 

-> chủ đề khác có thể làm việc ở giữa những streps

"Atomic" operations consist of one only one step 

-> đề khác không thể thực hiện công việc trong khi hoạt động nguyên tử được xử lý, các hoạt động nguyên tử luôn luôn xử lý như toàn bộ

Các đan cài class là một tập hợp các hoạt động nguyên tử như vậy và cho phép luồng an toàn theo định nghĩa. Thậm chí với nhiều chủ đề thực hiện các hoạt động đọc và ghi trên cùng một biến, các hoạt động này hoàn toàn an toàn.

Vẫn là sự kết hợp của các lệnh an toàn chủ đề đó có thể không an toàn, vì điều kiện chủng tộc có thể xảy ra giữa các hoạt động nguyên tử. Vì vậy, nếu bạn muốn ví dụ so sánh 2 biến và sau đó tăng một biến nhỏ hơn, đây không phải là luồng an toàn mặc dù các hoạt động đơn lẻ cho chính chúng là (interlocked.compare, interlocked.increment). Tại đây bạn vẫn phải sử dụng đồng bộ hóa.

Khác với giới hạn đó không có "mặt xấu ẩn" được khóa liên động.

Một ví dụ cho một racecondition với a = 5:

Thread1: a+=1 
Thread2: a+=2  
--> supposed to be 8, but can be only 6 or 7, 
but can also be 6 or 7 depending on which thread wins the race 

tùy chọn 1:

T1 step 1: read 5 
T1 step 2: add 1 = 6 
T1 step 3: write 6 
T2 step 1: read 6 
T2 step 2: add 2 = 8 
T2 step 3: write 8 
--> is 8 as supposed 

hoặc phương án 2:

T1 step 1: read 5 
T2 step 1: read 5 
T1 step 2: add 1 = 6 
T2 step 2: add 2 = 7 
T2 step 3: write 7 
T1 step 3: write 6 
--> is only 6 

hoặc tùy chọn 3:

Với interlocked.increment:

tùy chọn 1:

T1 step 1: read 5, add 1, write 6 
T2 step 1: read 6, add 2, write 8 

hoặc phương án 2:

T2 step 1: read 5, add 2, write 7 
T1 step 1: read 7, add 1, write 8 

-> trong mọi trường hợp a = 8 như vụ, giải pháp threadsafe

Tất cả các câu hỏi được đăng ở đây có thể được giải quyết bằng cách áp dụng ví dụ đơn giản này cho mã có vấn đề.

Hy vọng điều này sẽ giúp những người khác làm chủ đề này trên google. Janis