Using collections wisely in asynchronous operations

Devashish Dhamne
Villa Plus Engineering
3 min readFeb 27, 2024

Let’s start this with a example:
I want to calculate squares of numbers from 1 to 15000. I decide to do this as an asynchronous operation as it might take lot of time to do this sequentially.

Take a look at code in the image:
GetNumbers() gets us the numbers from 1 to 15000 in a list.
GetSquares() returns the list of squares of all the 15000 numbers.
Let’s say there was a API endpoint which calculated the square of the number, which might take around 100 ms to respond. To simulate this, I have introduced a delay in CalculateSquare().

Now let’s run this code:

Notice something? the squares list only has square of 14474 numbers.
Now you might be wondering what happened there CalculateSquare() should have been called for all 15000 numbers and it should have actually calculated all 15000 squares.
Yes, you are right it did calculate squares of 15000 numbers individually.
There’s a global variable numberOfCalls which I have used to prove the same. Have a look at 3rd line in console output.
So, if it calculated the squares of 15000 numbers, why does the list squares have only 14474 numbers, what happened with 526 which got missed.

So, this is where it all happened, the code in yellow is the culprit.
Now you must be wondering what’s wrong with Add(). Well, it is not protected by a lock. Here multiple threads are trying to add the value of square into squares collection at the same time, causing a race condition. That is the reason square values never got added into the collection.

Now, how do we resolve this?
We need some locking mechanism to ensure that multiple threads are not trying to perform operation on the collection at the same time.
It could be done by wrapping the squares.Add(); statement with lock(){}
Or
By using a thread-safe collection. Thread-safe collections are the ones who have their implicit locking mechanisms implemented.
I decide to go with BlockingCollection of System.Collections.Concurrent; library.

Here’s the modified code:

and let’s run this:

Hooray!!

I used this simplest of examples, so that it could be easy to understand for all readers. But imagine you using non-thread-safe collection without lock in a important use case, it might end up omitting some important records you which you wanted to be part of end collection.
So, it is very important to have a know how about any collection or for that moment any data structure you decide to use while solving a problem.

--

--

Devashish Dhamne
Villa Plus Engineering

Senior Software Developer @ Villaplus | 👨‍💻 Space and Technology enthusiast