Reputation: 3122
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.
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 Result
inline and then optimize away the _result
variable into a register and just read the same value over and over again.
In C# I could mark the result as volatile
and 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.Write
and Volatile.Read
methods. However those methods can not be used with 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.
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.
Upvotes: 2
Views: 7308
Reputation: 700322
Using Thread.MemoryBarrier
only 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.
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.
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
Upvotes: 5