Concurrency, Muti-threading, Multi-processing, Asynchronous Programming and Event Loop

The relationship between concepts like concurrency, multi-threading, multi-processing, asynchronous programming and event loop can be confusing. Especially what techniques can achieve concurrency. This post is an attempt to explain them in a simple and visual way.

First of all, concurrency is a concept that one has the ability to handle multiple tasks out-of-order. Here out-of-order is the key, it doesn’t mean they are handled at the same time, and it doesn’t mean they are started or ended at the same time. This captures a wide variety of techniques that can achieve concurrency, including multi-threading, multi-processing and asynchronous programming.

Now let’s look at what are multi-threading and multi-processing. I group them because they are similar compare to asynchronous programming and event loop. It’s pretty clear from the words that they allow to use multiple threads or processes to execute tasks at the same time. They are different techniques to achieve concurrency and what’s the difference is out of the scope of this post. You can refer to reference likes to find out the detail.

Asynchronous programming is a technique to defer certain tasks to a later time and allows the main thread to handle other tasks. The most common example is asynchronous I/O used by node.js. In node.js, it hands off I/O operation to OS kernel and defers the callback of I/O operation to a later time. This allows node.js to process other tasks immediately after the I/O operation is handed off to OS and doesn’t need to wait until the OS finishes the I/O task. This is the core part of concurrency of node.js. As you may know, Javascript and node.js are single-threaded. This shows you that even a single-threaded system can have concurrency. Some people may argue that asynchronous programming doesn’t provide concurrency, this depends on what’s the definition of task. If people define callbacks as independent tasks, then they are correct. Because as you will see in the diagram later when we talk about event loop, callbacks are nothing more than tasks that being pushed to an event queue. In this definition, tasks are not executed out-of-order. Conventionally, most people treat callbacks as a part of the original task that is deferred to be finished later. So they consider this task is broken into several parts and executed out-of-order among other tasks, which made such system concurrent. I agree with the statement that asynchronous programming provides concurrency.

Finally let’s look at event loop. Event loop is a design pattern that waits for and dispatches events in a system. When it is used with techniques like Asynchronous programming and multi-processing, it also provides concurrency to the system. And event loop is typically constructed with a queue(for example FIFO queue), a loop and workers. Let’s look at a simple event loop that only has one worker shown in the picture. Events are pushed to the event queue, and event loop pulls event from the event queue and dispatches them to the worker. In this case, let’s assume the queue is FIFO queue, in such a system, events are processed one by one without concurrency. If we add Asynchronous programming to the picture, as you can see the line at the bottom, the worker adds the callback event back to the event queue. This provides concurrency even though there is only one worker or thread. This is what happens when you call setTimeout(myCallback, 100). Behind the scene, the worker pushes the myCallback to the event queue after 100ms. Now you understand why myCallback passed to setTimeout() won’t be called exactly after 100ms? It’s because there might be other events in the queue that need to be processed before myCallback. The 100ms is the minimum time myCallback needs to wait until it’s executed. Now you also understand why people use setTimeout(myCallback, 0). This doesn’t mean myCallback will be called after 0ms meaning immediately. This is a technique to free the main worker(thread) to handle other events in the queue and defer myCallback to a later time.

Event Loop with Single Worker

Event loop can also use multi-threading. One can simply add more workers and a thread poll in the system as shown in the picture below. The only difference is now multiple events can be pulled from the event queue and handled at the same time by different workers in different threads.

Event Loop with Thread Pool

One can be very creative and extend the event loop as they see fit. Using node.js as an example, it divides the entire event loop into multiple phases, and each phase itself is like a smaller event loop. If you want to know more, refer to the reference at the end.

References:
Concurrency
Event Loop
Multi-threading
Multi-processing
Node.js Event Loop