@rkenmi - Asynchrony vs. Multithreading

Asynchrony vs. Multithreading


Asynchrony vs. Multithreading


Back to Top

Updated on January 13, 2021

Asynchrony

Asynchronous programming, also known as event-driven programming, is built on foundations of Futures/promises. The basic idea is that instead of having a thread wait for a blocked call to finish (i.e. a network call to fetch resources), the thread continues its execution and comes back to it when that call is finished.

There are various implementations for asynchronous programming. For example, Java 8's CompleteableFuture is an example where, when a blocking call needs to be made in the main thread, it spins up another thread (CompleteableFuture is based on the Runnable interface) to keep track of that call while the main thread continues with its execution. When the call is completed and the result is stored, the value can be retrieved from the CompletableFuture object.

Another example is Node.JS, which is a very popular serverside framework / library for Javascript that is usable outside of browsers. Node.JS is known to handle network requests asynchronously, but this is actually just an extension of the ECMAScript standard, where Promises are used. What is tricky about Javascript is that Javascript is, by nature, single-threaded (Web workers can sort of simulate additional threads, but they don't have access to the DOM). This means that it cannot spin up another thread to keep track of the Promises and populate it. Instead, what Javascript uses to allow asynchronous programming is the event loop, which in a nutshell, constantly checks if all Promises are fulfilled before resuming the single-threaded execution process. This is why Node.js is often stated to be event-driven, asynchronous, and single-threaded.

Multithreading

Multithreading is basically the idea of having the same resources be handled by more than one thread. This is usually accomplished with mutex or locks, i.e. semaphores and monitors. For this reason, multithreading is difficult to reason about and manage, since deadlock situations can arise and the code to write this might not be very straightforward. Debugging shared memory across multiple threads can be difficult.

The benefit of multithreading is mainly performance - distributing intensive work to other threads means that your main thread can do more meaningful work. For example, if the main thread is waiting a long time on your hard drive controller to read/write a file, you can instead have another thread handle that work so that your main thread can continue on with the rest of the code.

Multithreading also allows for better CPU utilization by making use of more cores. Each CPU core can have up to two virtual threads. This means that on a 16 core CPU processor, you can have up to 32 threads that can help distribute your work in a multithreaded application.

Reminder: A thread is not the same thing as a process. A process is normally used to denote an encapsulated block of application code, memory, data and other resources; the OS may spin up one or more threads to run this process. Thus, threads from one process cannot ever interact with threads from a different process.

Which wins?

Neither wins! But observe the differences:

Asynchronous Programming:

  • Normally doesn't utilize many CPU cores
  • Handle things when it arises - event driven loop
  • Allows a single server to handle more requests vs. a blocking, sequential flow of handling one request at a time

Multithreading:

  • Better CPU core utilization; each core can have 2 threads ready for work
  • More complex application logic; instead of writing your own, consider using a library/framework that already uses multithreading
  • Difficult thread management: need to handle deadlocks, mutexes
  • Difficult to debug
  • Each thread has a separate set of registers, stack, etc. that it maintains

Keep in mind that asynchronous programming and multithreading aren't exactly mutually exclusive depending on the platform; for example with Java 8's CompleteableFuture, asynchronous programming is actually achieved using multithreading. The best way to think of it is that these are just two different possible solutions to tackle the issue of blocking calls on a single-thread application, which more often than not, limits performance.


Article Tags:
system designnodejsasynchronyasynchronous programmingmultithreadingsingle threadedfuturepromisesevent loopJavascriptes6