🛒Of Tescos and semaphores 🚦
Tags: dotnet, csharp, performance, codenewbie
Published: 2021-05-16 19:00:28

I remember when the first lock down occurred, people were panic buying the most ridiculous things. However, even during the early stages, some Tesco stores managed to figure out a way to maintain order while chaos ensued in the outside world.

People would be complaining outside in the waiting lines, "why do we have to wait, let's just all get in it doesn't matter" 🤦‍♂️ Well, there's a reason to their mad ways, fellow customer. With this, the aim is to explain it from the staffs' and other customers' point of view using semaphores.

###Sema-what?
According to wiki: _"a semaphore is a variable or abstract data type used to **control access to a common resource** by multiple processes and avoid critical section problems in a concurrent system such as a multitasking operating system."_

It also has another meaning outside of tech terms but that just makes things all the more confusing, and it doesn't need to be. The part in bold is the job of a semaphore, it has the same responsibility as a Tesco employee controlling entry to the store (which could also be considered a common resource 🕺).

###Unregulated store layout
In this example below, we can see that the ``GetInAndShop`` method inside ``GettingPeopleInTheStore`` is where the person goes in and gets their shopping on, the lines after are when they're leaving the store. This method (in serious terms) represents the execution of asynchronous processes, which would commonly involve API calls etc.

```
public class Program
    {
        static void Main()
        {
            Task.WaitAll(GettingPeopleInTheStore().ToArray());
        }

        static IEnumerable<Task> GettingPeopleInTheStore()
        {
            Console.WriteLine("start");
            for (int i = 0; i < 300; ++i)
            {
                yield return GetInAndShop(i);
            }
        }

        static async Task GetInAndShop(int person)
        {
            await DoorToTheStore(person);

                Console.WriteLine($"Person {person} ::: left Tesco");
        }

        static Task DoorToTheStore(int person)
        {
            Console.WriteLine($"Person {person} shopping in Tesco 😁");
            return Task.Delay(250);
        }
    }
```

Nothing amazing about this, there's no limit and customers can generally abuse the store capacity with no repercussions. In reality, as much as the managers want as much money as possible, their upkeep and safety of other processes (ahem.. I mean customers or staff members) is priority. 

###A more regulated store entrance

This is where a process of control comes in the form of the ``_staffMembers`` who essentially act like bouncers, and they are the semaphores. 

_Please note that I am using SemaphoreSlim instead of Semaphore here as it's generally considered faster and lighter for smaller processes (if I'm not mistaken 👀)_

```
    public class Program
    {
        static SemaphoreSlim _staffMember = new SemaphoreSlim(5, 10); //this controls how many processes (customers) we can let in at once

        static void Main()
        {
            Task.WaitAll(GettingPeopleInTheStore().ToArray());
        }

        static IEnumerable<Task> GettingPeopleInTheStore()
        {
            Console.WriteLine("start");
            for (int i = 0; i < 20; ++i)
            {
                yield return GetInAndShop(i);
            }
        }

        static async Task GetInAndShop(int person)
        {
            try
            {
                await _staffMember.WaitAsync();
                await DoorToTheStore(person);
                
                Console.WriteLine($"Person {person} ::: left tesco");
                _staffMember.Release();
            }
            catch (Exception e)
            {
                Console.WriteLine("Damn, too many people here I'm leaving: " + e.Message);
            }
        }

        static Task DoorToTheStore(int person)
        {
            Console.WriteLine($"Person {person} shopping in tesco 😁");
            return Task.Delay(250);
        }
    }
```

We can see here that the constructor for ``SemaphoreSlim`` takes in an integer. This represents the amount of processes (or customers in our case) that are allowed to go past the semaphore and do what they need to do. Imagine that the ``await _staffMember.WaitAsync()`` represents that staff member at the entrance, while the ``_staffMember.Release()`` method is the one guarding the exit. The constructor argument is the rule that these staff will go by; if the manager says allow _x_ people in at a time then they do it etc. 

In this case the ``_staffMember`` lets the first 10 go in, they shop till they drop and time goes by (``Task.Delay(250)``). Some leave after a while and this is where the exit staff member lets the entrance one know via the``Release`` call 📲. They understand the message and proceed to let people in, all the while ensuring that the store has a maximum of 10 people in at one time (very small Tesco, it's in a mountain somewhere).

The result is that customers/processes can now come to do what they need to do, staff members at checkout are able to process orders as well as security being able to monitor customers more accurately. All the while, these semaphores are keeping the peace for an amazing shopping experience. 🛒🥙🥐🥘🥝🥔🥖🦐🥞🥗

Thanks for getting this far, let me know what you think!