Search code examples
vb.netmultithreadingspinlock

Behaviour Of Local Variables In A Multi-Threaded Method In VB.Net


The code below is part of an example from MSDN. It is the example on how to use SpinLock but to my eye there is a race condition in it. Why I think this is because of the Dim lockTaken As Boolean = False line in the UpdateWithSpinLock method. It appears to me that the following could occur:

Thread 1 enters UpdateWithSpinLock method and executes as far as _queue.Enqueue(d) and a context switch occurs.
Thread 1 now has the SpinLock and lockTaken is True.
Thread 2 enters and only executes as far as the line Dim lockTaken As Boolean = False.
Thread 2 has now set lockTaken back to False and a context switch occurs.
Thread 1 continues and tests lockTaken in the Finally block and finds it to be False (it should be True for Thread 1) so doesn't release the SpinLock.
Thread 1 exits the method leaving the lock inplace and thread 2 waiting forever.

Imports System.Threading
Imports System.Threading.Tasks

Class SpinLockDemo2

    Const N As Integer = 100000
    Shared _queue = New Queue(Of Data)()
    Shared _lock = New Object()
    Shared _spinlock = New SpinLock()

    Class Data
        Public Name As String
        Public Number As Double
    End Class

    Shared Sub Main()
        UseSpinLock()

        Console.WriteLine("Press a key")
        Console.ReadKey()

    End Sub

    Private Shared Sub UpdateWithSpinLock(ByVal d As Data, ByVal i As Integer)

        Dim lockTaken As Boolean = False
        Try
            _spinlock.Enter(lockTaken)
            _queue.Enqueue(d)
        Finally

            If lockTaken Then
                _spinlock.Exit(False)
            End If
        End Try
    End Sub

    Private Shared Sub UseSpinLock()
        Dim sw = Stopwatch.StartNew()

        Parallel.Invoke(
               Sub()
                   For i As Integer = 0 To N - 1
                       UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
                   Next
               End Sub,
                Sub()
                    For i As Integer = 0 To N - 1
                        UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
                    Next
                End Sub
            )
        sw.Stop()
        Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds)
    End Sub
End Class

Is the way I'm interpreting this correct. If it's not could you please show me what I'm missing.


Solution

  • In that code, lockTaken is a local variable so no instance of that method running in one thread could change the value of that variable in another instance of the method running on a different thread. What you're describing would require that lockTaken was a member variable.