2009-05-30 17 views
6

Tôi đang cố gắng viết một phiên bản không có khóa của một hàng đợi cuộc gọi mà tôi sử dụng để gửi tin nhắn. Đây không phải là cho bất cứ điều gì nghiêm trọng, chỉ để tìm hiểu về luồng.Làm cách nào để chỉ định tương đương với biến động trong VB.net?

Tôi tương đối chắc chắn mã của tôi là chính xác, trừ khi các hướng dẫn được sắp xếp lại hoặc thực hiện trong sổ đăng ký. Tôi biết tôi có thể sử dụng các rào cản bộ nhớ để ngừng đặt hàng lại, nhưng làm thế nào tôi có thể đảm bảo các giá trị được ghi vào bộ nhớ ngay lập tức?

Public Class CallQueue 
    Private first As New Node(Nothing) 'owned by consumer' 
    Private last As Node = first 'owned by producers' 
    Private Class Node 
     Public ReadOnly action As Action 
     Public [next] As Node 
     Public Sub New(ByVal action As Action) 
      Me.action = action 
     End Sub 
    End Class 

    Private _running As Integer 
    Private Function TryAcquireConsumer() As Boolean 
     Threading.Thread.MemoryBarrier() 

     'Dont bother acquiring if there are no items to consume' 
     'This unsafe check is alright because enqueuers call this method, so we never end up with a non-empty idle queue' 
     If first.next Is Nothing Then Return False 

     Threading.Thread.MemoryBarrier() 

     'Try to acquire' 
     Return Threading.Interlocked.Exchange(_running, 1) = 0 
    End Function 
    Private Function TryReleaseConsumer() As Boolean 
     Do 
      Threading.Thread.MemoryBarrier() 

      'Dont release while there are still things to consume' 
      If first.next IsNot Nothing Then Return False 

      Threading.Thread.MemoryBarrier() 

      'Release' 
      _running = 0 

      Threading.Thread.MemoryBarrier() 

      'It is possible that a new item was queued between the first.next check and releasing' 
      'Therefore it is necessary to check if we can re-acquire in order to guarantee we dont leave a non-empty queue idle' 
      If Not TryAcquireConsumer() Then Return True 
     Loop 
    End Function 

    Public Sub QueueAction(ByVal action As Action) 
     'Enqueue' 
     'Essentially, this works because each node is returned by InterLocked.Exchange *exactly once*' 
     'Each node has its .next property set exactly once, and also each node is targeted by .next exactly once, so they end up forming a valid tail' 
     Dim n = New Node(action) 
     Threading.Interlocked.Exchange(last, n).next = n 

     'Start the consumer thread if it is not already running' 
     If TryAcquireConsumer() Then 
      Call New Threading.Thread(Sub() Consume()).Start() 
     End If 
    End Sub 
    Private Sub Consume() 
     'Run until queue is empty' 
     Do Until TryReleaseConsumer() 
      first = first.next 
      Call first.action() 
     Loop 
    End Sub 
End Class 

Trả lời

3

Tôi không phải là chuyên gia về chủ đề này để hy vọng người khác sẽ sửa lỗi nếu tôi sai. Từ những gì tôi hiểu, vấn đề tối ưu hóa bộ nhớ hiện nay là một lý thuyết và không nhất thiết phải là một cái gì đó sẽ xảy ra trong thực tế. Nhưng có nói rằng, tôi nghĩ rằng bằng cách sử dụng API liên khóa để truy cập bộ nhớ của bạn (bất kể MemoryBarrier), bạn sẽ không bị ảnh hưởng.

Thật không may là không tương đương với biến động trong VB.NET. Nó không được trang trí với một thuộc tính bình thường, mà là một trình biên dịch đặc biệt được tạo ra từ trình sửa đổi. Bạn sẽ cần phải sử dụng Reflection để phát ra một kiểu với kiểu trường này.

Đây là tài nguyên tôi thường đề cập đến khi tôi có câu hỏi về luồng trong khuôn khổ .NET. Nó rất dài nhưng hy vọng bạn sẽ thấy nó hữu ích.

http://www.yoda.arachsys.com/csharp/threads/printable.shtml

+0

Lý thuyết? Giống như, bạn có nghĩa là, phần quan trọng không phải là kẻ giết người thực hiện tuyệt đối cho 512 máy CPU? – EFraim

10

Không có tương đương với volatile từ khóa C# 's trong VB.NET. Thay vào đó, những gì thường được khuyến nghị là sử dụng MemoryBarrier. Các phương pháp trợ giúp cũng có thể được viết:

Function VolatileRead(Of T)(ByRef Address As T) As T 
    VolatileRead = Address 
    Threading.Thread.MemoryBarrier() 
End Function 

Sub VolatileWrite(Of T)(ByRef Address As T, ByVal Value As T) 
    Threading.Thread.MemoryBarrier() 
    Address = Value 
End Sub 

Cũng có một blog hữu ích post về chủ đề này.

+1

Hữu ích, nhưng tôi vẫn còn bối rối tại sao rào cản bộ nhớ đọc đến sau thay vì trước đó, và ngược lại để viết. –

+0

@Strilanc: từ tài liệu trên câu trả lời dưới đây: Mọi đọc xảy ra sau khi đọc dễ bay hơi trong chuỗi lệnh diễn ra sau khi đọc dễ bay hơi trong mô hình bộ nhớ - chúng không thể sắp xếp lại trước khi đọc dễ bay hơi.Một ghi dễ bay hơi đi theo chiều ngược lại - mọi chữ viết xảy ra trước khi ghi dễ bay hơi trong chuỗi lệnh diễn ra trước khi ghi dễ bay hơi trong mô hình bộ nhớ. – EFraim

-1

Bạn cũng có thể viết một thuộc tính cho "bay hơi" sử dụng Thread.VolatileRead() và Thread.VolatileWrite() và làm cho tất cả các thuộc tính/biến với thuộc tính như:

<Volatile()> 
Protected Property SecondsRemaining as Integer 

Wrote ở đâu đó nhưng dường như không thể tìm thấy nó ngay bây giờ ...

2

Bắt đầu từ .NET 4.5, họ đã thêm hai phương pháp mới vào BCL để mô phỏng từ khóa volatile: Volatile.ReadVolatile.Write. Chúng hoàn toàn tương đương với việc đọc/ghi một trường volatile. Bạn có thể sử dụng chúng rõ ràng trong VB.NET. Chúng là tốt hơn (trong đó tốt hơn == nhanh hơn) so với Thread.VolatileRead/Thread.VolatileWrite vì chúng sử dụng một nửa hàng rào thay vì hàng rào đầy đủ.

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