在线程之间正确共享变量 (.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
Correctly sharing variables between threads (.NET/VB.NET)
提问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.Write和Volatile.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

