So yesterday we came to the conclusion that [even simple things can be more difficult][1] than we think when threads are involved.
In our case we had a class that incremented a value and one instance of that class was being invoked by multiple threads. And we found out that even i+= 1 is not an atomic operation.
But what happens when you swap the threads for an Async-Await syntax, Does it become more easy.
Apparently it isn’t easier for me. It took me quit a bit longer to find the right way of writing this.
Firstly you need to look at this.
Imports System.Threading
Module Module1
Private incre As New Incrementing
Sub Main()
Console.WriteLine("Starting")
Const numberofthreads As Integer = 20
Dim ts(numberofthreads) As Task(Of Integer)
For i = 0 To numberofthreads
ts(i) = count()
Next
Task.WaitAll(ts)
For i = 0 To numberofthreads
Console.WriteLine(ts(i).Result)
Next
Console.ReadLine()
End Sub
Async Function count() As Task(Of Integer)
For i = 1 To 1000
Await incre.Add()
Thread.Sleep(1)
Next
Return incre.Read()
End Function
Public Class Incrementing
Private i As Integer = 0
Public Async Function Add() As Task(Of Integer)
i += 1
Return 0
End Function
Public Function Read() As Integer
Return i
End Function
End Class
End Module```
Do you think the above runs Async?
You probably guessed it. It doesn’t.
As you can see by this result.
> Starting
> 1000
> 2000
> 3000
> 4000
> 5000
> 6000
> 7000
> 8000
> 9000
> 10000
> 11000
> 12000
> 13000
> 14000
> 15000
> 16000
> 17000
> 18000
> 19000
> 20000
> 21000
It’s all nice and sync.
The problem here is the for each in the count method. We can add a Task.Run to that do make it do what we want.
So second attempt.
```vbnet
Imports System.Threading
Module Module1
Private incre As New Incrementing
Sub Main()
Console.WriteLine("Starting")
Const numberofthreads As Integer = 20
Dim ts(numberofthreads) As Task(Of Integer)
For i = 0 To numberofthreads
ts(i) = count()
Next
Task.WaitAll(ts)
For i = 0 To numberofthreads
Console.WriteLine(ts(i).Result)
Next
Console.ReadLine()
End Sub
Async Function count() As Task(Of Integer)
Await Task.Run(Async Function()
For i = 1 To 1000
Await incre.Add()
Thread.Sleep(1)
Next
End Function)
Return incre.Read()
End Function
Public Class Incrementing
Private i As Integer = 0
Public Async Function Add() As Task(Of Integer)
Await Task.Run(Sub() i += 1)
Return 0
End Function
Public Function Read() As Integer
Return i
End Function
End Class
End Module```
The result however is wrong
> Starting
> 14824
> 16243
> 10212
> 10336
> 15125
> 14166
> 11188
> 13141
> 17146
> 16582
> 16945
> 13803
> 15913
> 17088
> 17189
> 20408
> 20426
> 20309
> 20930
> 20741
> 20896
So it is very easy to find all kinds of ways that don’t work and give freaky results.
Actually getting the result I wanted wasn’t that difficult.
But it still required using a lock.
This one will get us the correct result.
```vbnet
Imports System.Threading
Module Module1
Private incre As New Incrementing
Sub Main()
Console.WriteLine("Starting")
Const numberofthreads As Integer = 20
Dim ts(numberofthreads) As Task(Of Integer)
For i = 0 To numberofthreads
ts(i) = count()
Next
Task.WaitAll(ts)
For i = 0 To numberofthreads
Console.WriteLine(ts(i).Result)
Next
Console.ReadLine()
End Sub
Async Function count() As Task(Of Integer)
Await Task.Run(Sub()
For i = 1 To 1000
incre.Add()
Thread.Sleep(1)
Next
End Sub)
Return incre.Read()
End Function
Public Class Incrementing
Private i As Integer = 0
Public Sub Add()
Interlocked.Increment(i)
End Sub
Public Function Read() As Integer
Return i
End Function
End Class
End Module```
With this as one of the possible results.
> Starting
> 8442
> 8490
> 8504
> 8495
> 8494
> 8541
> 8534
> 8531
> 13026
> 17454
> 17496
> 17504
> 17490
> 17496
> 17527
> 17527
> 17504
> 19515
> 20993
> 21000
> 21000
Where it is important to note that we are only interested in the end result and not on how it gets there.
Threading is lots of fun as you can see. And getting it right can be non-trivial.
[1]: /index.php/DesktopDev/MSTech/threads-are-always-tricky-even