In this post, I am going to discuss the difference between single threading, multi-threading, synchronous and asynchronous processes.
Threading is one of the must-know concepts that soon or late a programmer needs to address, especially if he is working on software that needs to scale. If you are still working on little projects, it is difficult to understand the need for allocating your work in threads.
Instead, when you start approaching Cloud Computing, where everything you use has a cost, and when multiple clients will start using your applications, you will both have the need of optimizing the computing power at your disposal and making sure that you can have a dedicated algorithm stream per user.
For example, if 1000 users decide to use your application simultaneously, you cannot expect to have 1000 virtual machines operating at the same time for the sole purpose of addressing every user’s needs: the costs will be exorbitant, for no reason, and this approach would not be scalable. Instead, you would allocate as many users you can on each virtual machine optimizing resources and allowing every virtual machine to run a dedicated process for each user.
To make this plan a reality, you need to understand how threading works. In this article, I am only going to focus on the technical explanations related to threading, and I will not yet indulge in the code for every variation.
All threading options
To better guide you in understanding how threading works, here is a table that will summarize all its possible variations that I will use throughout this post:
What is a thread vs. tasks?
There is a huge difference between threads and tasks. You can think of tasks as promises, while threads are the ways of fulfilling that promise. For each thread, you can assign one or more tasks that will be executed one after the other. For example, printing 100 numbers in a row is a task, while its container is a thread. The same thread can contain a second task that instructs the program to print all the letters in the alphabet. Because they are under the same thread, they cannot run at the same time.
Single threading vs. multi-threading
The first concept we need to understand in threading is the difference between running things on a thread vs. multiple threads. With a single thread, each task has to finish the previous one to run, as it cannot be allocated on another thread. The majority of the algorithms you write, especially when starting, will follow this principle. Especially when you are using a single computer there are no extra resources to allocate for other processes.
With multi-threads, instead, the tasks are allocated on different threads that can run simultaneously. Remember, you do not need synchronicity for the threads to run at the same time (at least in python, synchronicity is dealt in a completely different way and means a completely different thing): once you have multiple threads, they run simultaneously.
Synchronous vs. Asynchronous
The need to improve the level of your code using an asynchronous library is only required in very specific use cases. Even when you have to perform heavy computations and you have enough computing power to spare, multi-threading is sufficient.
Synchronicity means that once a process has started, there is no way to interrupt it with the user input. You can still manually force the algorithm to stop its execution, but this would require a console, and is not manageable while thousands of people are accessing your virtual machine simultaneously. Synchronicity is fine when once a process has started, you have no longer the need to interrupt it. For example, the processes that only last a fraction of a second are necessarily synchronous because the user would not even have the physical time to interrupt it.
Asynchronicity, instead, allows users to interrupt running processes with a request. This can become very useful when long processes are running. For example, I have been creating a discord bot called pomodoro-mick (I hope you have seen Rick and Morty TV series to get the reference).
This software allows you to initiate a timer that monitors your working schedule for the next 2 hours and tells you when to take breaks. However, what if you want your personalized timer to stop, preventing it to keep sending you notifications? That is a perfect case for using synchronicity and stopping the process while it’s still running. While synchronicity still uses functions, synchronicity uses coroutines.