Как мне указать эквивалент volatile в VB.net? - PullRequest
7 голосов
/ 30 мая 2009

Я пытаюсь написать версию очереди вызовов без блокировки, которую я использую для передачи сообщений. Это не для чего-то серьезного, просто чтобы узнать о многопоточности.

Я относительно уверен, что мой код верен, за исключением случаев, когда инструкции переупорядочены или сделаны в регистрах. Я знаю, что могу использовать барьеры памяти, чтобы прекратить переупорядочение, но как я могу обеспечить немедленную запись значений в память?

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

Ответы [ 6 ]

11 голосов
/ 30 мая 2009

В VB.NET нет эквивалента ключевому слову C # volatile. Вместо этого часто рекомендуется использовать MemoryBarrier . Вспомогательные методы также могут быть написаны:

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

Также есть полезный блог пост на эту тему.

6 голосов
/ 09 ноября 2010

Используйте методы Thread.VolatileRead() и VolatileWrite() из BCL.

http://msdn.microsoft.com/en-us/library/system.threading.thread.volatileread.aspx

4 голосов
/ 16 августа 2015

Начиная с .NET 4.5, они добавили два новых метода в BCL для имитации ключевого слова volatile: Volatile.Read и Volatile.Write . Они должны быть полностью эквивалентны чтению / записи поля volatile. Вы можете четко использовать их в VB.NET. Они лучше (где лучше == быстрее ), чем Thread.VolatileRead / Thread.VolatileWrite, поскольку они используют половину заборов вместо полных заборов.

3 голосов
/ 30 мая 2009

Я не эксперт в этом вопросе, поэтому, надеюсь, кто-то еще исправит меня, если я ошибаюсь. Из того, что я понимаю, вопрос оптимизации памяти в настоящее время является теоретическим и не обязательно должен происходить в реальности. Но, сказав это, я думаю, что использование Interlocked API для доступа к вашей памяти (независимо от MemoryBarrier) вас не затронет.

К сожалению, в VB.NET нет эквивалента для volatile. Он не украшен обычным атрибутом, а является специальным модификатором, сгенерированным компилятором. Вам потребуется использовать Reflection для создания типа с таким полем.

Вот ресурс, на который я часто ссылаюсь, когда у меня возникают вопросы о многопоточности в .NET Framework. Это очень долго, но, надеюсь, вы найдете это полезным.

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

0 голосов
/ 20 июня 2018

Код считывателя Mono.Cecil делает FieldType As RequiredModifierType с ModifierType как System.Runtime.CompilerServices.IsVolatile.

0 голосов
/ 20 декабря 2013

Вы также можете написать атрибут для «Volatile», используя Thread.VolatileRead () и Thread.VolatileWrite (), и сделать все свойства / переменные с этим атрибутом такими:

<Volatile()>
Protected Property SecondsRemaining as Integer

Написал это где-то, но сейчас не могу найти его ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...