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




Reply With Quote