I know you all read [8 things you probably didn’t know about C#][1]. But there’s a million things about threads you didn’t know and then some would be a nice title for a blogpost too.
It’s this little line that should draw your attention.
i = 1 is atomic (thread-safe) for an int but not long
And that is good to know (for 32-bit systems anyway).
But don’t mistake the above with this line.
i += 1
That line is not threadsafe.
You can easily verify that by doing this.
Imports System.Threading
Module Module1
Private incre As New Incrementing
Sub Main()
For i = 1 To 10
Dim t1 As New Thread(AddressOf count)
t1.IsBackground = True
t1.Start()
Dim t2 As New Thread(AddressOf count)
t2.IsBackground = True
t2.Start()
Next
Console.ReadLine()
End Sub
Sub count()
For i = 1 To 1000
incre.Add()
Thread.Sleep(1)
Next
Console.WriteLine(incre.Read())
End Sub
Public Class Incrementing
Private i As Integer = 0
Public Sub Add()
i += 1
End Sub
Public Function Read() As Integer
Return i
End Function
End Class
End Module
You would expect to get to 20000 here because you are doing 20000 time i += 1, right.
Well no.
18342
18359
18404
18437
18444
18444
18444
18473
18479
18490
18490
18491
18494
18514
18520
18520
18522
18525
18525
18527
this was one of the results I got.
Doesn’t make sense?
Well sure it does.
because i += 1 is actually 3 operations (at least).
Read i, add 1 to i and then store i. And I did not take that into account.
So at several points in time two or more threads might be reading the same value of i and thus we never get to 20000.
There are of course several ways around this.
We could synclock the i += 1 operation.
Public Sub Add()
SyncLock lockobject
i += 1
End SyncLock
End Sub```
or we could use Interlocked.Increment(i)
```vbnet
Public Sub Add()
Interlocked.Increment(i)
End Sub```
And now the results are more inline with what I expected.
> 19857
> 19863
> 19877
> 19887
> 19902
> 19906
> 19932
> 19932
> 19941
> 19948
> 19955
> 19960
> 19967
> 19974
> 19989
> 19992
> 19995
> 19997
> 19999
> 20000
[1]: http://damieng.com/