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.
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.