Dalibor Čarapić
Dalibor Čarapić

Reputation: 3122

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

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

Answers (1)

Guffa
Guffa

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

Related Questions