在线程之间正确共享变量 (.NET/VB.NET)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29162694/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 18:53:53  来源:igfitidea点击:

Correctly sharing variables between threads (.NET/VB.NET)

.netvb.netmultithreadingsynchronization

提问by Dalibor ?arapi?

I've been reading a lot of various blogs and StackOverflow questions to try to find an answer to my question and at the end I could not find anything so I thought to ask the question myself.

我一直在阅读很多不同的博客和 StackOverflow 问题,试图找到我的问题的答案,但最后我找不到任何东西,所以我想自己问这个问题。

I am building an application where I have a long running worker threads which perform some operations and places some results into variables during its run. The worker thread is constantly working and is constantly producing some results whereby I am only interested in the 'current' value of the result. At some point another thread will get the 'current' result of the worker thread run and do something with it.

我正在构建一个应用程序,其中我有一个长时间运行的工作线程,它执行一些操作并将一些结果在运行期间放入变量中。工作线程不断工作并不断产生一些结果,因此我只对结果的“当前”值感兴趣。在某些时候,另一个线程将获得工作线程运行的“当前”结果并对其进行处理。

A simple implementation of such worker thread can be done like this:

这种工作线程的简单实现可以这样完成:

Public Class Worker

    Private _result As DateTimeOffset?

    Public ReadOnly Property Result As DateTimeOffset?
        Get
            Return _result
        End Get
    End Property


    Public Sub ThreadMethod()
        ' Do something
        _result = x
        ' .
        ' .
        _result = y
        ' .
        ' .
        ' etc
    End Sub

End Class

Now lets say I have I a consumer which wants to consume the result:

现在假设我有一个想要消费结果的消费者:

Public Class Consumer

    Private _worker As Worker

    Public Sub Consume()
        Dim result As DateTimeOffset?
        While True
            ' Do some work on your own
            ' .
            ' .
            ' Get current result
            result = _worker.Result
            ' Do something with result

        End While
    End Sub

End Class

As far as I have understood how compilers work it seems logical that the compiler could make the Property Resultinline and then optimize away the _resultvariable into a register and just read the same value over and over again. In C# I could mark the result as volatileand this (I think) would prevent such optimization but the keyword does not exist in VB.NET. The closest solution I could come up with is to use Volatile.Writeand Volatile.Readmethods. However those methods can not be used with Nullable(Of DateTimeOffset).

据我了解编译器的工作原理,编译器可以进行Property Result内联,然后将_result变量优化到寄存器中,然后一遍又一遍地读取相同的值,这似乎是合乎逻辑的。在 C# 中,我可以将结果标记为volatile,这(我认为)会阻止这种优化,但关键字在 VB.NET 中不存在。我能想到的最接近的解决方案是使用Volatile.WriteVolatile.Read方法。但是,这些方法不能与Nullable(Of DateTimeOffset).

Note 1: Although this question seems to be related to weak/strong memory model and usage of memory barriers I do not believe that they have any impact in this scenario. That being said if somebody states that Thread.MemoryBarrier()would force the read/write to be from memory that it is also a good enough solution for me.

注 1:虽然这个问题似乎与弱/强内存模型和内存屏障的使用有关,但我不相信它们在这种情况下有任何影响。话虽如此,如果有人声明Thread.MemoryBarrier()会强制读/写来自内存,那么这对我来说也是一个足够好的解决方案。

Note 2: I am aware that there are other ways to solve the problem but I would like to know if the solution is possible (if the problem even exists, I am not even sure about that) for the described scenario.

注 2:我知道还有其他方法可以解决问题,但我想知道对于所描述的场景是否有可能解决(如果问题存在,我什至不确定)。

回答by Guffa

Using Thread.MemoryBarrieronly ensures the order of memory accesses in that thread. What you have is two threads that access the same variable, so you need more protection than that.

使用Thread.MemoryBarrieronly 确保该线程中的内存访问顺序。您拥有的是访问同一变量的两个线程,因此您需要更多的保护。

The varable is not atomic, so if you don't synchronise the accesses you may end up reading a partially written value. You can make a synchronised getter and setter for the property, and then use the setter in the worker thread to set the value instead of setting the backing variable.

变量不是原子的,因此如果您不同步访问,您最终可能会读取部分写入的值。您可以为该属性制作一个同步的 getter 和 setter,然后在工作线程中使用 setter 来设置值而不是设置后备变量。

Private _result As DateTimeOffset?
Private _sync as New Object()

Public Property Result As DateTimeOffset?
    Get
        SyncLock _sync
            Return _result
        End SyncLock
    End Get
    Set(ByVal value as dateTimeOffset?)
        SyncLock _sync
            _result = Value
        End SyncLock
    End Set
End Property