如何在 VB.net 中指定 volatility 的等效项?

发布于 2024-07-22 19:08:12 字数 2509 浏览 10 评论 0原文

我正在尝试编写用于消息传递的调用队列的无锁版本。 这并不是为了任何严肃的事情,只是为了了解线程。

我相对确定我的代码是正确的,除非指令被重新排序或在寄存器中完成。 我知道我可以使用内存屏障来停止重新排序,但如何确保值立即写入内存?

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

I'm attempting to write a lock-free version of a call queue I use for message passing. This is not for anything serious, just to learn about threading.

I'm relatively sure my code is correct, except if the instructions are re-ordered or done in registers. I know I can use memory barriers to stop re-ordering, but how can I ensure values are written to memory immediately?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

拥抱我好吗 2024-07-29 19:08:12

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

还有一个有用的博客 post关于这个话题。

There's no equivalent of C#'s volatile keyword in VB.NET. Instead what's often recommended is the use of MemoryBarrier. Helper methods could also be written:

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

Also there's a useful blog post on this subject.

世界和平 2024-07-29 19:08:12

使用 BCL 中的 Thread.VolatileRead() 和 VolatileWrite() 方法。

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

Use Thread.VolatileRead() and VolatileWrite() methods from the BCL.

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

怀念你的温柔 2024-07-29 19:08:12

从 .NET 4.5 开始,他们向 BCL 添加了两个新方法来模拟 volatile 关键字:Volatile.ReadVolatile.Write 。 它们应该完全等同于读取/写入易失性字段。 您可以在 VB.NET 中清楚地使用它们。 它们更好(其中更好 == 更快)比Thread.VolatileRead/Thread.VolatileWrite 因为他们使用半栅栏而不是全栅栏。

Starting in .NET 4.5, they added two new methods to the BCL to simulate the volatile keyword: Volatile.Read and Volatile.Write. They should be totally equivalent to reading/writing a volatile field. You can clearly use them in VB.NET . They are better (where better == faster) than the Thread.VolatileRead/Thread.VolatileWrite because they use half fences instead of full fences.

默嘫て 2024-07-29 19:08:12

我不是这个主题的专家,所以如果我错了,希望其他人能纠正我。 据我了解,内存优化问题目前只是一个理论上的问题,不一定会在现实中发生。 但话虽如此,我认为通过使用 Interlocked API 进行内存访问(无论 MemoryBarrier 如何),您不会受到影响。

不幸的是,VB.NET 中没有 volatility 的等效项。 它没有用普通属性修饰,而是一个特殊的编译器生成的修饰符。 您需要使用反射来发出具有此类字段的类型。

当我对 .NET 框架中的线程有疑问时,我经常参考以下资源。 它很长,但希望您会发现它很有用。

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

I am not an expert on this subject so hopefully someone else will correct me if I am wrong. From what I understand, the issue of memory optimizations is presently a theoretical one and not necessarily something that will occur in reality. But having said that, I think that by using the Interlocked API for your memory access (regardless of the MemoryBarrier) you would not be affected.

Unfortunately there is not an equivalent for volatile in VB.NET. It's not decorated with a normal attribute, but is rather a special compiler generated modifier. You would need to use Reflection to emit a type with this kind of field.

Here is a resource I often refer to when I have questions about threading in the .NET framework. It's very long but hopefully you will find it useful.

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

乜一 2024-07-29 19:08:12

Mono.Cecil 阅读器代码使 FieldType As
requiredModifierType,ModifierType 为 System.Runtime.CompilerServices.IsVolatile。

The Mono.Cecil reader code makes the FieldType As
RequiredModifierType with the ModifierType as System.Runtime.CompilerServices.IsVolatile.

心舞飞扬 2024-07-29 19:08:12

您还可以使用 Thread.VolatileRead() 和 Thread.VolatileWrite() 为“Volatile”编写一个属性,并使所有属性/变量都具有该属性,例如:

<Volatile()>
Protected Property SecondsRemaining as Integer

将此写在某处,但现在似乎无法找到它...

You can also write an attribute for "Volatile" using Thread.VolatileRead() and Thread.VolatileWrite() and make all properties/variables with that attribute like:

<Volatile()>
Protected Property SecondsRemaining as Integer

Wrote this somewhere but can't seem to find it right now...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文