This weekend I watched a video on YouTube(see comments for link) that said there is a bug in the Random class when used with threading. It was in C# so I thought I'd recreate it in VB. I was surprised to find it does fail. And after failing the first time it is like the Random is broken.
To use this code create a Form with three buttons and two labels with default names.
Button 1 is a sanity check and only fails after Button 2 fails.
Here is what I did to fix? this issue,Code:' Is this https://www.youtube.com/watch?v=WRB4OHpSXHs
' true?
' .Net 4.8
'
' Strictly speaking zero values aren't errors.
' Documentation says this about .Next
' A 32-bit signed integer that is greater than or equal to 0 and less than Int32.MaxValue.
' but ...
Private Shared PRNG As New Random
Private LRand As Concurrent.BlockingCollection(Of Integer)
Const Tries As Integer = 100000
Const Thrds As Integer = 25
Private STPW As New Stopwatch
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SetUp()
For x As Integer = 1 To Tries * Thrds
LRand.Add(PRNG.Next)
Next
STPW.Stop()
Dim Nzro As Integer
Nzro = (From i In LRand Where i > 0 Select i).Count
Dim zct As Integer = LRand.Count
If Nzro <> zct Then
Label1.Text = "1 ERR " & (Nzro / zct).ToString("p1") 'per cent of non-zero values
Else
Label1.Text = "Good " & Nzro.ToString("n0")
End If
Label2.Text = STPW.Elapsed.TotalMilliseconds.ToString("n1")
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
SetUp()
Parallel.For(1, Thrds + 1,
Sub(idx As Integer)
For zx As Integer = 1 To Tries
Do While Not LRand.TryAdd(PRNG.Next)
Threading.Thread.Sleep(0)
Loop
Next
End Sub)
STPW.Stop()
Dim Nzro As Integer
Nzro = (From i In LRand Where i > 0 Select i).Count
Dim zct As Integer = LRand.Count
If Nzro <> zct Then
Label1.Text = "2 ERR " & (Nzro / zct).ToString("p1") 'per cent of non-zero values
Else
Label1.Text = "Good " & Nzro.ToString("n0")
End If
Label2.Text = STPW.Elapsed.TotalMilliseconds.ToString("n1")
End Sub
Private Sub SetUp()
' PRNG = New Random
Label1.Text = ""
LRand = New Concurrent.BlockingCollection(Of Integer)
STPW.Restart()
End Sub
Code:Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
SetUp()
Parallel.For(1, Thrds + 1,
Sub(idx As Integer)
For zx As Integer = 1 To Tries
Do While Not LRand.TryAdd(MyPRNG.NextI)
Threading.Thread.Sleep(0)
Loop
Next
End Sub)
STPW.Stop()
Dim Nzro As Integer
Nzro = (From i In LRand Where i > 0 Select i).Count
Dim zct As Integer = LRand.Count
If Nzro <> zct Then
Label1.Text = "3 ERR " & (Nzro / zct).ToString("p1") 'per cent of non-zero values
Else
Label1.Text = "Good " & Nzro.ToString("n0")
End If
Label2.Text = STPW.Elapsed.TotalMilliseconds.ToString("n1")
End Sub
Private Class MyPRNG
Private Shared Block As New Threading.SpinLock '(True)
Private Shared _PRNG As New Random
Public Shared Sub NextB(buffer() As Byte)
Block.Enter(Nothing)
_PRNG.NextBytes(buffer)
If Block.IsHeld Then
Block.Exit()
End If
End Sub
Public Shared Function NextD() As Double
Block.Enter(Nothing)
NextD = _PRNG.NextDouble
If Block.IsHeld Then
Block.Exit()
End If
End Function
Public Shared Function NextI() As Integer
Block.Enter(Nothing)
NextI = _PRNG.Next
If Block.IsHeld Then
Block.Exit()
End If
End Function
Public Shared Function NextI(maxValXclu As Integer) As Integer
Block.Enter(Nothing)
NextI = _PRNG.Next(maxValXclu)
If Block.IsHeld Then
Block.Exit()
End If
End Function
Public Shared Function NextI(minValIlu As Integer, maxValXclu As Integer) As Integer
Block.Enter(Nothing)
NextI = _PRNG.Next(minValIlu, maxValXclu)
If Block.IsHeld Then
Block.Exit()
End If
End Function
End Class

