You also are confused by these terminologies? I was too. But not anymore.
Let's understand them with the help of an example and along with that I'll explain when exactly you should be looking to use them in this mini-article, so you don't have to go through all YouTube videos or articles on the Internet.
Say, you have a GUI app, MVP right now so single-threaded. Right now this thread is usually called, the main thread, which is also a UI thread (all operations asked by the user on GUI are performed on the main thread).
Currently, our app is Synchronous.
Each task inside a single thread do tasks one by one, if any one task is longer the other tasks can’t proceed until that task is done.
Synchronous/single-threaded is best useful for small apps, CLI app, or any program that don’t do any computationally expensive task.
Say, that one task like fetching a database (or an I/O operation), will take a long time, waiting to retrieve the data. The UI will freeze; no buttons will work until data is fetched.
The whole process is still single-threaded. BUT, fetching the database for data will now be done asynchronously, the task is going to be done by Database, and the main thread will just say this task is Async and needs to be awaited. It will pass the task a callback function to callback when the data is retrieved and the task will return a promise (in JS) or a future (in Rust) which needs to be polled from time to time (in Rust since futures are lazy) done by the main thread until drawn to completion. By the time, the main thread gets a future it goes on executing other tasks and does not wait for that task to complete. To programmers, this still looks synchronous.
Other than tasks that can be delegated to external hardware, Async can also be used with tasks that are OK to be done in a single-threaded application and requires CPU processing power or tasks which are basically waiting, which won’t make UI unresponsive (or stop other tasks from continuing for some waitable period) and less computationally expensive but may take a little bit of time. In Python async can also be used to delegate task that can be handled by lower level code (like in C/C++) that can do multiprocessing themselves.
If our GUI app is doing some task in the background such as parsing a GIANT file, or encoding/decoding a video (if done on CPU) which takes a lot of time and can’t be delegated to hardware (like Database/disk controller in the previous case) then the UI will freeze and stop responding.
The solution is to spawn another thread, another road to the CPU that can do tasks independent of what's going on in the thread it was spawned by. Both threads (or many) will continue doing their own assigned tasks and are going to be part of the App's process. Inside each thread, the tasks can then be done synchronously or asynchronously as required.
In scenarios like this, use Mutli-threading. Delegate the task that requires the processor’s time and is ULTRA computationally expensive to another thread. Threads also share the same resources as the whole process (the whole App is a single process, which can have multiple threads in itself.).
Keep in mind though multi-threading is expensive, since spawning a new thread takes some resources, and switching between them as well. So, if possible stay with async. Unless you are into coroutines, which are lightweight threads.
Now, our UI will be responsive (running on the UI thread), while in the background we do video encoding or any expensive task (on the Main thread).
In this we spin up unique processes, each can have many threads but is usually kept single-threaded. They all have their own separate address space, separate memory, and separate resources, and communicate between them using Inter-process communication.
Plus you can spin them on multiple machines separate from your local host machine.
This is best left for a huge data-intensive application program.
That's all for today. Hope you learned something new.
This is Anurag Dhadse, signing off.