Last modified: July 29, 2024

This article is written in: 🇺🇸

Basic terminology

Let's start by defining some key terms and emphasizing the distinctions between related concepts. In general those concepts are universal and may be applied to any programming language. The differences between the languages will be discussed in greater detail later, when we attempt to explain the specific approach in implementing concurrency.

Sequential vs Non-sequential Execution

The way tasks are executed in a program can be classified as either sequential or non-sequential. Understanding these concepts is crucial for effective programming and optimizing performance.

Sequential Execution

In sequential execution, tasks are carried out in the exact order they appear in the code. Each task must be completed before the next one begins. This approach is straightforward and ensures that operations dependent on previous results are handled correctly.

Characteristics of Sequential Execution:

Non-sequential Execution

Non-sequential execution allows tasks to be executed in any order, as the correct results do not depend on the order of execution. This approach is often used in situations where tasks are independent of each other.

Characteristics of Non-sequential Execution:

Concurrency vs Parallelism

Concurrency and parallelism are concepts related to the execution of multiple tasks but differ in their implementation and purpose.

Concurrency

Concurrency refers to the design of a program to handle multiple tasks at the same time. It involves structuring the program to manage several tasks that make progress independently, though not necessarily simultaneously.

Characteristics of Concurrency:

Parallelism

Parallelism involves performing multiple tasks simultaneously. This is achieved by dividing a task into smaller sub-tasks that can be executed at the same time on multiple processors or cores.

Characteristics of Parallelism:

Synchronous vs Asynchronous Execution

Synchronous and asynchronous execution describe how tasks are initiated and completed in relation to other tasks.

Synchronous Execution

Synchronous execution requires that one task must be completed before the next one can start. This method is straightforward but can lead to inefficiencies if tasks involve waiting periods.

Characteristics of Synchronous Execution:

Asynchronous Execution

Asynchronous execution allows tasks to be initiated and run independently of each other. This method is non-blocking and can improve efficiency, especially for tasks involving I/O operations or network requests.

Characteristics of Asynchronous Execution:

Comparison of Execution Paradigms

Characteristic Sequential Non-Sequential Concurrent Parallel Asynchronous Synchronous
Definition Tasks are executed one after another in a specific order. Tasks do not follow a strict order of execution. Multiple tasks make progress within overlapping time periods. Multiple tasks are executed simultaneously on multiple processors. Tasks are initiated and proceed independently of other tasks, often with callbacks or promises to handle completion. Tasks are executed one after another, where each task waits for the previous one to complete.
Order of Execution Strict and predictable order. Flexible order. Overlapping time periods, but not necessarily simultaneous. Simultaneously on different processors. Initiated independently, can complete at any time. Strict and predictable order, waiting for each task to complete before starting the next.
Task Scheduling Simple and straightforward. Flexible, can change dynamically. Managed by the scheduler to ensure progress of all tasks. Managed by the system, often requires multiple processors. Can be scheduled to run at any time, often managed by event-driven programming models. Requires each task to wait for the previous one to finish.
Complexity Low complexity, easy to design and debug. More complex due to flexibility. Moderate complexity, requires careful management of resources. High complexity, requires handling of synchronization and resource sharing. Moderate to high complexity, requires handling of callbacks, promises, or other mechanisms to manage independent tasks. Low complexity, but can lead to inefficiencies if tasks have to wait for long periods.
Performance Can be slower due to waiting for each task to complete before starting the next. Can be efficient if tasks are independent and do not need a specific order. Can improve performance by overlapping tasks, but may involve context switching overhead. Can significantly improve performance for CPU-bound tasks by using multiple processors. Can improve responsiveness and resource utilization, especially in I/O-bound tasks. Can lead to inefficiencies if tasks involve a lot of waiting, as each task has to wait for the previous one to complete.
Use Cases Simple scripts, batch processing where order is crucial. Event-driven systems, where tasks can be handled as they come. Multithreading applications, GUI applications where multiple tasks need to be handled concurrently. High-performance computing, data processing tasks that can be divided into smaller, independent tasks. Web servers handling multiple requests, applications with I/O operations where waiting for responses would be inefficient. Systems where order of operations is crucial and tasks are dependent on the completion of previous tasks, such as transaction systems.

Process

A process is an instance of a program that is currently executing on a computer. It represents a program in action, containing the program code and its current activity. When you use any application on your computer, such as a web browser, text editor, or video player, it runs as a process. In some cases, a single application can have multiple processes running simultaneously to handle different tasks more efficiently. Additionally, the operating system uses processes to perform tasks in the background while you work, such as managing system resources, running scheduled tasks, or handling network communications.

Characteristics of a Process

Role of the Operating System (OS)

The operating system is responsible for managing all processes running on the computer. It ensures that each process gets the necessary resources and time to execute its tasks while maintaining the overall efficiency and stability of the system.

Key Responsibilities of the OS in Process Management:

Process Table

The OS maintains a table known as the process table, which keeps track of all processes' status. This table contains information about each process, including its PID, state, priority, program counter, memory allocation, and other attributes. The process table allows the OS to efficiently manage and switch between processes.

States of a Process

A process can be in one of several states during its lifecycle. The three primary states are:

Thread

In computer science, a thread refers to a sequence of instructions that can be executed concurrently with other threads within a program. A process can be comprised of one or more threads, with each thread being provided with its own program counter (PC), register set, and stack space. Threads share resources such as code section, data section, and OS resources such as open files and signals with other threads within the same process. Threads are sometimes called lightweight processes because they have some similarities to processes.

Threads can be used to improve the performance and concurrency of programs. For example, a text editor can employ several threads, one to format the text, another to receive inputs, and so on. The use of multiple threads within a program can allow multiple operations to be performed simultaneously, which can lead to faster execution times and improved performance.

Role of the Operating System (OS)

The operating system (OS) plays a pivotal role in the management of threads and processes. It acts as an intermediary between the hardware and the software applications, ensuring efficient and fair allocation of system resources. Here's a detailed explanation of how the OS manages threads and processes:

Thread Management and Scheduling

Memory Allocation in a Program

A program's memory allocation is often divided into four segments:

Processes vs Threads

A process is an independent instance of a program in execution. It contains the program code and its current activity. Processes are isolated from each other and do not share memory space directly, which makes them suitable for running independent applications.

Characteristics of Processes:

A thread is the smallest unit of execution within a process. Multiple threads can exist within the same process and share the same memory space, which allows them to access the same data and resources efficiently.

Characteristics of Threads:

Key Differences Between Processes and Threads:

Aspect Process Thread
Independence Processes are independent instances that run in separate address spaces. Threads are subsets of a process and run within the same address space as the process.
Memory Processes have separate address spaces, meaning each process has its own memory area. Threads share the address space of their parent process, allowing them to access the same memory and data.
Communication Processes require Inter-Process Communication (IPC) mechanisms like pipes, message queues, or shared memory to communicate with each other. Threads can communicate directly by accessing shared memory within the process.
State Information Processes carry considerable state information, including process ID, process state, memory information, and more. Threads maintain minimal state information, typically just a thread ID, program counter, register set, and stack.
Resource Sharing Processes do not share resources directly with each other; each process has its own resources. Threads share resources such as code, data, and open files with other threads within the same process.
Creation Overhead Process creation has higher overhead because it involves allocating a separate memory space and other resources. Thread creation has lower overhead since threads share resources and memory with the parent process.
Context Switching Context switching between processes involves more overhead due to switching separate memory spaces and state information. Context switching between threads is faster and involves less overhead because they share the same memory space and resources.

Here's a table comparing the life cycle stages of threads and processes:

Life Cycle Stage Thread Life Cycle Process Life Cycle
New Thread is in the process of being created. Process is in the process of being created.
Ready Thread is ready to run when CPU is available. Process is ready to execute when CPU is available.
Running Thread is actively executing instructions. Process is actively executing instructions.
Waiting/Blocked Thread is waiting for resources or I/O. Process is waiting for resources or I/O.
Timed Waiting Thread is waiting for a specified time. (Not typically a separate state, but can be considered under waiting)
Terminated Thread has completed execution or is aborted. Process has completed execution or is terminated.
Ready Suspended (Not typically a separate state, but can be inferred from 'Ready' and 'Blocked') Process is in secondary storage and ready to execute when moved to main memory.
Blocked Suspended (Not typically a separate state, but can be inferred from 'Blocked') Process is in secondary storage and waiting for an event before moving to main memory.

Practical Implications:

CPU-Bound vs I/O-Bound

To optimize a software program's performance, it's crucial to identify the primary source of slowdown or the bottleneck. This bottleneck could either be due to I/O (input/output) activities or an underutilized CPU. Different strategies can be applied based on the cause of the bottleneck. Understanding the nature of the bottleneck helps in choosing the right optimization techniques to enhance the software's performance.

CPU-Bound

When a task's completion time mainly depends on the speed of the CPU, it is considered CPU-bound. In simpler terms, the CPU's processing power determines how quickly the task finishes. A task is CPU-bound if it spends most of its time executing computations rather than waiting for I/O operations.

Key Characteristics of CPU-Bound Tasks:

Strategies for Optimizing CPU-Bound Tasks:

Example of a CPU-Bound Scenario:

I/O waiting
CPU Processing  ----Task 1---->----Task 2---->

In this example, the CPU is continuously processing tasks with minimal waiting for I/O operations. The speed at which tasks are completed is primarily limited by the CPU's processing power.

I/O-Bound

A task is I/O-bound when its completion time mostly relies on the time spent waiting for input/output operations to finish. In simpler terms, the efficiency of I/O components determines how fast the task is completed. A task is I/O-bound if it spends most of its time waiting for I/O operations, such as reading from or writing to a disk, network communication, or other I/O devices.

Key Characteristics of I/O-Bound Tasks:

Strategies for Optimizing I/O-Bound Tasks:

Example of an I/O-Bound Scenario:

I/O waiting         -----request----->     ------request------>     ------request------>
CPU Processing  --->                  ---->                    ---->

In this example, the CPU is often waiting for I/O operations to complete before it can continue processing. The speed at which tasks are completed is primarily limited by the efficiency of the I/O operations.

Table of Contents

  1. Basic terminology
    1. Sequential vs Non-sequential Execution
      1. Sequential Execution
      2. Non-sequential Execution
    2. Concurrency vs Parallelism
      1. Concurrency
      2. Parallelism
    3. Synchronous vs Asynchronous Execution
      1. Synchronous Execution
      2. Asynchronous Execution
    4. Comparison of Execution Paradigms
  2. Process
    1. Characteristics of a Process
    2. Role of the Operating System (OS)
    3. Process Table
    4. States of a Process
  3. Thread
    1. Role of the Operating System (OS)
      1. Thread Management and Scheduling
      2. Memory Allocation in a Program
  4. Processes vs Threads
  5. CPU-Bound vs I/O-Bound
    1. CPU-Bound
    2. I/O-Bound