So yesterday we came to the conclusion that even simple things can be more difficult 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.
```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)
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.