Как мне указать эквивалент volatile в VB.net?
-
06-09-2019 - |
Вопрос
Я пытаюсь написать версию очереди вызовов без блокировки, которую я использую для передачи сообщений.Это не для чего-то серьезного, просто чтобы узнать о потоковой обработке.
Я относительно уверен, что мой код верен, за исключением случаев, когда инструкции переупорядочены или выполняются в регистрах.Я знаю, что могу использовать барьеры памяти, чтобы остановить переупорядочивание, но как я могу гарантировать, что значения будут немедленно записаны в память?
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
Решение
Использование Thread.VolatileRead()
и VolatileWrite()
методы из BCL.
http://msdn.microsoft.com/en-us/library/system.threading.thread.volatileread.aspx
Другие советы
Эквивалента C # не существует volatile
ключевое слово в VB.NET.Вместо этого часто рекомендуется использовать Носитель памяти.Вспомогательные методы также могут быть написаны:
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
Также есть полезный блог Публикация по этому вопросу.
Начиная с .NET 4.5, они добавили в BCL два новых метода для имитации volatile
ключевое слово: Изменчивый.Читать и Изменчивый.Напишите.Они должны быть полностью эквивалентны чтению / записи volatile
поле.Вы можете четко использовать их в VB.NET .Они такие лучше (где лучше == быстрее) , чем тот Thread.VolatileRead
/Thread.VolatileWrite
потому что они используют половинчатые заборы вместо полных.
Я не эксперт в этом вопросе, поэтому, надеюсь, кто-нибудь еще поправит меня, если я ошибаюсь.Насколько я понимаю, проблема оптимизации памяти в настоящее время является теоретической и не обязательно чем-то, что произойдет в реальности.Но, сказав это, я думаю, что использование Interlocked API для вашего доступа к памяти (независимо от MemoryBarrier) вас не затронет.
К сожалению, в VB.NET нет эквивалента для volatile.Он не украшен обычным атрибутом, а скорее является специальным модификатором, сгенерированным компилятором.Вам нужно было бы использовать Отражение, чтобы создать тип с таким типом поля.
Вот ресурс, на который я часто ссылаюсь, когда у меня возникают вопросы о потоковой обработке в .NET framework.Это очень долго, но, надеюсь, вы найдете это полезным.
http://www.yoda.arachsys.com/csharp/threads/printable .shtml
Код Mono.Cecil reader преобразует тип поля в RequiredModifierType с типом модификации в System.Runtime.CompilerServices.Является энергонезависимым.
Вы также можете написать атрибут для "Volatile", используя Thread .VolatileRead() и Thread.VolatileWrite() и придать всем свойствам / переменным с этим атрибутом вид:
<Volatile()>
Protected Property SecondsRemaining as Integer
Где-то я это писал, но, похоже, не могу найти прямо сейчас...