Home > OS > Understanding Synchronous/Asynchronous and Blocking/Non-blocking

Understanding Synchronous/Asynchronous and Blocking/Non-blocking
OS

Understanding Synchronous/Asynchronous and Blocking/Non-blocking


In programming, Synchronous/Asynchronous and Blocking/Non-blocking are concepts that are often confused. This article explores the differences between these two sets of concepts and examines how they are applied through a file reading example.

Synchronous and Asynchronous


Focuses on the flow of tasks and their completion times.

  • Synchronous: Tasks proceed sequentially, and the next task starts only after receiving the completion result of the current task.
  • Asynchronous: Tasks proceed simultaneously or independently, allowing the next task to start without waiting for the current task to complete. Callbacks, signals, etc., are used to notify the completion of tasks.

Sync, Async

Blocking and Non-blocking


Focuses on whether the caller waits during a function call.

  • Blocking: When a function is called, the caller loses control and waits until the task is completed. During this time, no other tasks can be performed.
  • Non-blocking: The caller immediately regains control after the function call and can perform other tasks. The caller does not wait.

Blocking, Non-blocking

Combining Synchronous/Asynchronous and Blocking/Non-blocking


Synchronous + Blocking

The combination of synchronous and blocking means that when a function is called, it waits until the task is completed before executing the next piece of code. This is the typical function call method where operations proceed sequentially.

Synchronous + Blocking

Python Code Example

def synchronous_blocking_read():
    print("Starting file read")
    with open('example.txt', 'r') as f:
        data = f.read()
    print("File read complete")
    print("Data:", data)

print("Main program starts")
synchronous_blocking_read()
print("Main program ends")

Output:

Main program starts
Starting file read
(Waiting while reading the file)
File read complete
Data: (File content is printed)
Main program ends

Code Explanation:

  • Uses the open() function to open the file and the read() method to read its contents.
  • The program waits until the entire file is read before proceeding to the next step.
  • After the task is completed, the next code is executed sequentially.

Synchronous + Non-blocking

This is typically used with polling techniques, where the state is periodically checked. The caller immediately regains control, but the task proceeds sequentially.

Synchronous + Non-blocking

Python Code Example

In this code, BlockingIOError may not occur depending on the file system and operating system. It is generally used with I/O streams like sockets and pipes, but for illustrative purposes, a file read example is provided.

import os
import errno
import time

def synchronous_nonblocking_read():
    print("Starting file read")
    fd = os.open('example.txt', os.O_RDONLY | os.O_NONBLOCK)
    data = b''
    while True:
        try:
            chunk = os.read(fd, 1024)
            if not chunk:
                break
            data += chunk
        except BlockingIOError as e:
            if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
                print("Waiting for data...")
                time.sleep(0.1)  # Wait briefly before retrying
                continue
            else:
                raise
    os.close(fd)
    print("File read complete")
    print("Data:", data.decode())

print("Main program starts")
synchronous_nonblocking_read()
print("Main program ends")

Output:

Main program starts
Starting file read
Waiting for data...
Waiting for data...
Waiting for data...
File read complete
Data: (File content is printed)
Main program ends

Code Explanation:

  • Uses os.open() to open the file in non-blocking mode (os.O_NONBLOCK).
  • Attempts to read the file with os.read(). If the data is not ready, a BlockingIOError is raised.
  • The exception handler checks for EAGAIN or EWOULDBLOCK error codes and retries after a short wait.
  • Once the file is fully read, the loop exits and the file is closed.

Asynchronous + Blocking

The task is invoked asynchronously but waits until it is completed. This approach does not take advantage of the benefits of asynchronous processing and is therefore inefficient.

Asynchronous + Blocking

Asynchronous + Non-blocking

The task is invoked asynchronously, and the caller immediately regains control to perform other tasks. This allows for efficient resource utilization as other tasks can be executed while the asynchronous task is in progress.

Asynchronous + Non-blocking

Python Code Example

import asyncio
import aiofiles

async def asynchronous_nonblocking_read():
    print("Starting file read")
    async with aiofiles.open('example.txt', 'r') as f:
        data = await f.read()
    print("File read complete")
    print("Data:", data)

async def main():
    print("Main program starts")
    task = asyncio.create_task(asynchronous_nonblocking_read())
    print("Performing other tasks...")
    await task  # Wait until the file read is complete
    print("Main program ends")

asyncio.run(main())

Output:

Main program starts
Performing other tasks...
Starting file read
(File is read while other tasks can continue)
File read complete
Data: (File content is printed)
Main program ends

Code Explanation:

  • Uses aiofiles.open() to open the file asynchronously.
  • await f.read() allows the event loop to perform other tasks while reading the file.
  • asyncio.create_task() schedules the task and immediately returns control.
  • While the file is being read, “Performing other tasks…” is printed, demonstrating that other operations can continue.

Summary of Concepts


Synchronous vs Asynchronous

  • Synchronous focuses on the order and dependencies of tasks.
  • Asynchronous indicates that tasks can proceed independently.

Blocking vs Non-blocking

  • Blocking indicates whether the caller waits during the function call.
  • Non-blocking indicates whether the caller immediately regains control after the function call.

When to Use Each Approach


  • Synchronous Blocking: Suitable for simple scripts or sequential tasks due to its straightforward implementation.
  • Synchronous Non-blocking: Used when introducing asynchronous processing is difficult. Commonly used with polling.
  • Asynchronous Blocking: An inefficient approach that is rarely used.
  • Asynchronous Non-blocking: Used to enhance performance in large-scale network requests or I/O operations by efficiently utilizing resources.

Conclusion


Synchronous/Asynchronous and Blocking/Non-blocking are crucial concepts in programming that significantly impact system performance and efficiency.
Proper understanding and appropriate application of each concept can optimize a program’s responsiveness and resource utilization.