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

82 Views Asked by At

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.

1

There are 1 best solutions below

4
jmcilhinney On BEST ANSWER

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.