Threads in Operating System

Thread-in-OS-feature-image.jpg

A thread is the basic unit of execution that can be scheduled and managed by an operating system on a CPU. Threads allow you to run a huge program in your IDE while still being able to browse the documentation in your web browser, without having to worry about your computer hanging or freezing up. Threads enable smooth multitasking because they share resources from the system and execute tasks concurrently. In this article, we will examine thread in OS, what they are, types of threads, why they are useful, how they enable parallel processes, and some of the common issues and models associated with threads.

Table of Contents:

What is Parallel Processing in OS?

A program is usually very large and requires more time of CPU time to execute such a process. The more a CPU executes only one process, the more it will affect the efficiency and the speed of processes, and affect the user experience. This challenge is tackled by parallel processing, which assists the applications and programs written in the modern world to run and enables the advancement of technology.

Now, the programs are written such that they can be divided into sub-processes, and these sub-processes run in parallel on the CPU. A CPU has many cores inside its structure, and all the subprocesses run in parallel on these cores. Take, for example, the scenario below, where the while loop will run 10000000 times and calculate the sum, which will take a lot of time.

A process in OS
  • To parallelize this, we will break this process into four to match the number of processes.
  • We will do this logically by dividing the while loop into four processes:
    • 1 to 2500000.
    • Then from 2500001 to 5000000.
    • Then from 5000001 to 7500000.
    • And finally from 7500001 to 10000000.
Division to ensure Parallel Processing in OS

But this method also causes overhead, as we have to use a system call to create the sub-processes, and then combining the results (Inter-Process Communication) will additionally add to the overhead. This is where the concept of threading comes into play.

Launch Your Software Engineering Career
Master in-demand tools and technologies with real-world training and placement support.
quiz-icon

What is Thread in OS?

A process can be divided into threads, which are lightweight units of execution used to achieve parallel processing in an operating system. Threads enhance CPU performance by sharing the process’s resources, such as memory space and file descriptors, thus avoiding the overhead associated with creating entirely separate processes, which are isolated and require independent system stacks and contexts.

Components of a Thread

A thread has three basic components: stack space, register set, and Program Counter.

  • Stack Space: Holds the thread’s local variables, function call information, and return addresses.
  • Register Set: Contains temporary data and intermediate results used during the thread’s execution.
  • Program Counter: Keeps track of the next instruction to be executed by the thread.
Multithreaded process for thread in OS

Why Do We Need a Thread in OS?

  • Threads ensure better CPU performance by allowing different parts of a program to run simultaneously on multiple CPU cores, speeding up execution and giving better performance.
  • CPU idle time is minimized because threads can work while others wait. Priority can also be assigned to a thread, and the highest scheduled thread gets executed first.
  • Threads keep applications responsive and enhance user experience. For example, in a web browser, one thread can render the page while another handles user input.
  • Threads of the same process share memory and system resources, which makes communication between them faster and easier compared to processes.

Types of Thread in OS

There are two types of threads: user-level threads and kernel-level threads.

1. User-Level Threads (ULT)

These threads are managed by the user space without the knowledge of the kernel.

Advantages

  • With ULT, thread creation, termination, and context switching are faster because they don’t involve the kernel.
  • These threads do not rely on OS support and therefore can work across different operating systems.
  • Given that all the operations occur in the user space, there is no kernel context switching. This contributes towards the reduction of context switching overhead that comes from switching to the kernel mode.
  • Developers can implement custom thread scheduling, creating their own thread management methodologies and then implementing them.

Disadvantages

  • Since only one user-level thread executes per process, there is no actual parallel execution occurring, even on a multicore CPU.
  • If a thread makes a blocking system call, the entire process gets blocked and can be unresponsive. This will lead to your process not completing and unwanted preemption as well.
  • User-level threads do not take advantage of multiple processors properly since there is no kernel support, and hence do not perform proper “multiprocessing”.
  • They have limited system integration and therefore ULTs can’t take advantage of kernel-level optimizations, such as load balancing, resource allocation, etc.

2. Kernel-Level Threads (KLT)

Kernel-level threads are managed directly by the operating system. Every thread is recognised and managed by the kernel.

Advantages

  • Multiple threads running on different processors at the same time enable genuine parallel execution.
  • The operating system’s control over threads allows it to perform optimized scheduling of CPU resources, together with enhanced memory allocation. This results in more efficient system resource management.
  • Multiple threads within a process remain operational even when one thread becomes blocked.
  • The kernel provides better management of blocking operations, such as I/O, because it can handle them more effectively.

Disadvantages

  • The kernel must intervene to manage threads; the overhead caused by the switching can slow down process execution.
  • Slower thread management compared to user-level threads due to system call overhead.
  • Requires more complex operating system support, increasing kernel complexity and system resource usage.
  • Higher memory consumption, as each thread may require separate kernel resources (like stacks or thread control blocks).

Difference Between Process and Thread in OS

A process and a thread in OS may look similar, but they are quite different from each other.

Feature Process Thread
Definition A process is an independent program in execution. A thread is the smallest unit of execution within a process.
Resource Allocation Has its own memory space, files, and resources. Shares memory and resources with other threads in the same process.
Communication Requires inter-process communication (IPC). Threads can communicate easily through shared memory.
Creation Overhead Higher, more time, and system resources are needed. Lower; thread creation is lightweight.
Execution Can run independently of other processes. Runs as part of a process; multiple threads can run concurrently.
Context Switching Slower (involves switching memory and resources). Faster (shares resources, only register/context needs switching).
Fault Isolation The crash of one process doesn’t affect others. If one thread crashes, it can bring down the whole process.
Parallelism Processes can run in parallel on different CPUs. Threads in the same process can also run in parallel.
Examples Chrome browser and MS Word are running separately. One thread for UI, another for background tasks within the same app.

Multithreading in an Operating System

Multithreading has multiple threads of the same process executing concurrently on the CPU. Individual threads will run separately, but they will share the same resources of the process, like memory, code, file handles, etc. It allows a program to do many things concurrently, so it can improve responsiveness and utilize the CPU efficiently.

For example, with a web browser using multithreading:

  • One thread is rendering the web page content
  • One thread is handling user input
  • Another thread is downloading images in the background

Benefits of Multithreading

  • Improved CPU usage: Creating multiple threads keeps the CPU busy; otherwise, the CPU would have been idle if a failed process were waiting for data or an I/O response.
  • Faster processing: You can divide a problem into multiple threads to execute it at once through parallel processing and finish it more quickly.
  • Responsiveness: Through multithreading, an application can perform resource-heavy work, which means that this thread will have the CPU for a long time (one of the cores of a CPU) in the background, while also performing other work, therefore remaining responsive.
  • Efficient resource sharing: Threading allows threads to share the memory and the system resources of the parent process, so you can use the resources more efficiently and have less overhead.
  • Less costly: Thread creation and context switches are cheaper and faster than processes.
  • Simplified program structure: Easier logical division of work, for example, UI thread, logic thread, and I/O thread.

Multithreading Models in OS

Multithreading models illustrate the relationship between user-level threads and kernel-level threads. Only kernel threads are managed by the operating system, so these multithreading models ultimately impact how threads are scheduled, run, and managed. The basic threading models are:

1. Many-to-One Model

In this model, it ignores the kernel-level thread and maps many user-level threads onto a single kernel-level thread. All of the management of threads, creation, synchronization, and scheduling is performed by the thread library from user space. The operating system cannot see these threads; hence, the operating system level can see the group of threads as only one thread.

Advantages

  • It is simple to implement and manage, as all of the thread operations are done in user space; there is no interaction with the kernel.
  • Thread creation and context switches are very fast because there are no syscalls.

Disadvantages

  • The greatest disadvantage is that true parallel execution is not permitted. This means that even on multi-core systems, only one thread can run at a time because only one kernel thread exists.
  • If any thread blocks, the entire process is blocked, regardless of all other threads.

Use Case

Early Java implementations (like Java Green Threads) on Solaris used the many-to-one model.

2. One-to-One Model

The One-to-One model represents a one-to-one relationship between kernel threads and user threads. Each user-level thread is mapped to an individual kernel thread. This means that the operating system can manage and schedule them individually.

Advantages

  • This model represents true parallelism as multiple threads can run simultaneously on separate cores or processors.
  • If one thread blocks due to a blocking system call from the external source, other threads can continue to execute. This improves responsiveness and performance.

Disadvantages

  • Creating and managing each thread uses kernel resources, resulting in more overhead.
  • Creation of threads is likely to be limited since the number of kernel resources may limit the number of threads a process can create.

Example Use Case

Linux, Windows, and modern Java Virtual Machines implement this model.

3. Many-to-Many Model

The Many-to-Many model maps many user-level threads to a lesser or equal number of kernel-level threads. This allows a great deal of flexibility because the system can generate many user threads while managing resource usage by limiting kernel thread creation.

Advantages

  • This model allows true concurrency because multiple threads can run at the same time on multiple processors.
  • If a user thread blocks, then other user threads will not be affected, because the OS can continue to schedule other kernel threads.
  • It strikes a good balance between performance and resource use, especially in systems that can support many threads.

Disadvantages

  • Implementing the many-to-many model is more difficult than the other models.
  • It may take more overhead than the many-to-one model because of the need to coordinate user and kernel threads.

Example Use Case

Some versions of Solaris and advanced Windows thread pool implementations have employed the many-to-many model.

Threading Issues in Operating Systems

In general, multithreading is a useful and beneficial programming practice that can achieve better responses and performance, but it is not without its challenges. The issues generally arise in the context of managing resources, synchronizing threads, and ensuring certain system calls are ‘thread safe’.

1. Thread Cancellation

Thread cancellation is the act of terminating a thread before the thread has completed execution. Cancellation is generally always requested by another thread in the same process.

Types of Thread cancellation:

  • Asynchronous Cancellation: The target thread is cancelled immediately. This could lead to resource leaks or an inconsistent state if the thread is cancelled in the middle of operating.
  • Deferred Cancellation: The target thread checks at certain cancellation points to check if it should cancel itself. This is the safer method, which will also clean up.

Issues:

  • Asynchronous cancellation could cause shared resources (memory, file descriptors) to become undefined.
  • Careful resource management while writing cleanup routines will prevent deadlocks and memory corruptions.

2. Thread-Local Storage (TLS)

Thread-local Storage provides each thread with its private copy of data, which is not shared among threads in the same process.

Importance:

  • TLS is useful when multiple threads may be executing the same code but need to track state that is specific to that thread.
  • It helps to avoid data races because thread-local variables do not need synchronization mechanisms to access.

Issues:

  • TLS may increase memory overhead.
  • Misuse may lead to confusion when data is assumed to be shared but isn’t, causing subtle bugs.

3. Signal Handling

Signals are a form of asynchronous notification delivered to a process (or thread) that lets it know something has happened, for example, an interrupt or illegal memory access.

Signal handling in multithreaded programs:

  • Single-threaded programs always have a single thread, and thus the signal can be delivered to it.
  • With a multithreaded program, there are multiple possibilities for sending the signal:
    • To a specific thread.
    • To whichever one of the threads (that is not blocking the signal).
    • To a designated signal-handling thread.

Issues:

  • It is not always obvious which thread will handle the signal.
  • Handling the signal incorrectly can lead to unexpected behaviour, especially if the signal interrupts a critical section or a system call.
  • Wrong usage of Thread-Local Storage can yield subtle bugs, especially if thread-local data is thought to be shared.

4. fork() and exec() System Calls

The fork() and exec() system calls are used to create a new process and to execute (replace one process) with another, respectively.

Behavior with Threads:

  • fork(): If calling this from a multithreaded process, then it is important to keep in mind that only the invoking thread will be duplicated in the child process; other threads will not be copied!
  • exec(): exec() replaces the entire process with a new program, so this will impact all threads, as the multithreaded (and the process) environment is all gone.

Consequences of Thread (Fork) Conflicts:

  • If using fork() in a multithreaded program, there could be some serious issues if there are other threads holding locks, as the locks will remain in a bad state in the child, basically inconsistent, as the locks held by other threads no longer exist in the child.
  • The general recommendation is to call fork() and execute an exec() function immediately to eliminate any threading issues.

5. Scheduler Activations

Scheduler activations are an abstraction layer designed to efficiently allow the many-to-many threading model of the user threads to be supported by a lesser number of kernel threads.

Goals:

  • Provides more fluid communication between the user-level threading library and the kernel scheduler.
  • Indicates any kernel events to the user-level library (for example, preemption or blocking), which allows the library to better handle the scheduling of threads.

Problems:

  • Scheduler activations add complexity to both the kernel and the user-level threading library.
  • Doing kernel event handling in user space requires detailed attention to avoid inconsistencies or race conditions.
Threading Issue Description
Thread Cancellation Terminating a thread, safely or abruptly. Risk of resource leaks or deadlocks.
Thread-Local Storage Provides threads with private data. Avoids sharing issues but increases memory usage.
Signal Handling Managing asynchronous signals in multithreaded processes. Can cause complex bugs.
fork() and exec() Process creation in multithreaded contexts can lead to inconsistencies.
Scheduler Activations Allows efficient user-thread scheduling in many-to-many models, but is complex.

Thread Libraries

When programmers want to create and manage threads for their applications, they typically don’t have to write the code directly communicating with the lowest levels of the operating system from scratch. They use thread libraries. You can think of a thread library as a specialized toolbox or as a set of pre-written instructions called an API (Application Programming Interface), which programmers use. It is a toolbox of all the required functions.

  • Creating a new thread.
  • Starting a thread so it begins its work.
  • Pausing or resuming a thread.
  • Joining threads (making one thread wait for another to finish).
  • Synchronizing threads (making sure threads don’t interfere with each other when accessing shared data).

Different operating systems and programming languages have their own preferred or standard thread libraries. Let’s look at some important ones:

And tools for things like:

Pthreads or POSIX Threads

  • What it is: Pthreads is the most common standard for creating and utilizing threads. “POSIX” means Portable Operating System Interface, which is a rule system for ensuring that software can work across various Unix-based operating systems. Pthreads are a common “rule system” for threads on these systems.
  • Where you find it: It’s primarily used on Unix-like operating systems such as Linux, macOS, and embedded systems. Programs created in C or C++ might often use the Pthreads directly.
  • Key Idea: Pthreads gives programmers very direct, low-level control over threads. This means you can fine-tune how threads behave, but it also requires you to manage many details manually.

Windows Threads

  • What it is: Just as Unix-like systems have Pthreads, Microsoft Windows has its specific way of handling threads, which we simply call Windows Threads. It’s Windows’ native thread library.
  • Where you find it: Programs built specifically for the Windows operating system use this library.
  • Key Idea: While Pthreads aims for portability across Unix-like systems, Windows Threads are tailored specifically for the Windows environment. They offer similar functionalities for creating and managing threads, but use Windows-specific functions and naming conventions.

Java Threads

  • What it is: Java is a programming language that offers different methods of creating and managing threads through the Java Virtual Machine (JVM), and you will not work with Pthreads or Windows Threads while writing your Java code; you would do this with Java’s Thread class and a Runnable interface.
  • Platform Independence: This is the biggest advantage of Java. When you write Java thread code, it behaves the same whether it runs on a Windows PC, a Linux server, or a macOS. The JVM automatically converts your Java thread commands to the appropriate underlying native OS threads (e.g., Pthreads on Linux, Windows Threads on Windows).
  • Higher-Level Control: Java’s thread library is “high-level”. In other words, it is generally easier to use than either Pthreads or Windows Threads, because the JVM hides many low-level, complicated details from you, so you can focus more on your application’s logic.
Feature Pthreads (POSIX Threads) Windows Threads Java Threads
What it is A widely accepted standard for threads. Microsoft Windows’ native thread API. Java language’s built-in thread system.
Primary Platforms Linux, macOS, Unix-like systems Microsoft Windows Any OS with a Java Virtual Machine (JVM) installed
Typical Language C, C++ C, C++, C# (for Windows apps) Java
Level of Control Low-level: Direct, fine-grained control over OS threads. Low-level: Direct interaction with Windows OS threads. High-level: JVM handles many complex details for you.
Code Portability High (across POSIX-compliant Unix-like systems). Low: Code is specific to Windows. Very High: “Write once, run anywhere.” JVM handles OS differences.
Who Manages Threads The programmers manages the thread via manual setup and control. Here as well, the program manages the thread. Primarily the JVM manages and optimizes threads automatically.
Think of it as A universal “how-to guide” for threads in Unix-like environments. Windows’ unique instruction manual for threads. Java’s smart “thread manager” that works everywhere.

Advantages and Disadvantages of Threads

Although threads make the execution and the interaction of a program more efficient and responsive, they also generate their own set of problems. It would be good for you, as a programmer, to recognize the benefits and both the problems and challenges of threads before using them.

Advantages of Using Threads

There are some good reasons to use thread in OS in modern applications. These are:

  • Better User Experience: Consider a program that needs to calculate a large matrix or download a large file. If this is a single-threaded program, the whole application might freeze while the long operation is in progress. A multi-threaded program can run the long operation in another thread and keep the main part of the application (the user interface, for example) responsive and available for user input.
  • Efficient Resource Sharing: Unlike separate processes (which have their own isolated memory spaces), threads within the same process share the same memory space and resources (like open files). This makes it very efficient for threads to communicate and share data, as they don’t need complex mechanisms to pass information between them.
  • Faster Execution (Concurrency): On modern computers with multi-core processors (CPUs with multiple “brains”), threads allow your program to truly run different parts of its code concurrently (at the same time). This means tasks that can be broken down into independent sub-tasks can be completed much faster by utilizing all available CPU cores.
  • Simplified Model for Concurrent Tasks: For certain types of problems, using threads can simplify the program’s design. Instead of building complex state machines to manage multiple operations, you can often assign a separate thread to each independent task, making the logic clearer.

Disadvantages of Using Threads

Despite their benefits, using a thread in OS introduces complexities that can lead to new problems if not handled carefully:

  • Increased Complexity and Debugging Challenges: Writing multithreaded code is inherently more complex than single-threaded code. It’s harder to predict the exact order in which threads will execute, which can lead to subtle bugs that are difficult to reproduce and fix. This makes debugging (finding and fixing errors) much more challenging.
  • Synchronization Overhead: Because threads share resources, there’s a risk of race conditions (where multiple threads try to access and modify the same data simultaneously, leading to unpredictable results). To prevent this, programmers must use synchronization mechanisms like locks or mutexes. While necessary, these mechanisms add overhead, potentially slowing down the program if overused or implemented poorly.
  • Deadlocks: A common and frustrating problem in multi-threaded programming is a deadlock. This occurs when two or more threads are stuck, each waiting for a resource that the other thread holds. It’s like two cars at a four-way stop, each waiting for the other to move, resulting in a standstill. Deadlocks can cause a program to freeze completely.
  • Context Switching Overhead: The operating system constantly switches between different threads to give each a slice of CPU time. This process, called context switching, involves saving the state of the current thread and loading the state of the next. While very fast, frequent context switching can introduce a small performance overhead, especially if there are too many threads.

Practical Example: Running Multiple Tasks with Threads

Here’s a simple example of how to run threads, showing two separate tasks happening “at the same time.”

Code:

Java

Output:

Running Multiple Tasks with Threads

Explanation: When you execute this Java code, you will notice that the messages from “Task A” and “Task B” will be intermixed (interleaved) in the console output. This is due to the fact that both tasks are running concurrently, and the Thread.sleep() Calls instruct the execution to pause before it continues to allow the other task to run. So, taking them together, you can see that threads create the illusion of allowing different parts of your program to run simultaneously when, in fact, they are running concurrently.

Transform into a Job-Ready Software Engineer - No Coding Background Needed!
Apply Now and turn your coding passion into a profession!
quiz-icon

Conclusion

Thread in OS is crucial to improving performance by allowing concurrent execution, getting the job done faster, and enabling the CPU to share resources. We discussed the various forms of threads, including user-level and kernel-level, as well as increased responsiveness due to parallel processing. Thread libraries quickly became evident as a valuable resource for controlling concurrent execution paths with different abstraction levels. Overall, threads help operating systems run tasks faster and smoothly, making better use of system resources.

Thread in Operating Systems – FAQs

Q1. What are threads in a computer system?

Threads are lightweight units of a process that can run independently. They share the same memory space but execute different tasks concurrently.

Q2. What is the thread system?

A thread system refers to the management of threads by an OS or programming environment, including creation, scheduling, and synchronization.

Q3. What is thread and kernel?

Threads may be user-level or kernel-level. Kernel threads are managed by the OS; user threads are managed by user-level libraries.

Q4. What are the types of threads?

Threads are of two main types: User-level threads and Kernel-level threads. They differ in how they are scheduled and managed.

Q5. What is @threads?

@threads is often a decorator or macro in programming (e.g., Julia language) used to enable multithreaded execution of a function or loop.

About the Author

Senior Consultant Analytics & Data Science, Eli Lilly and Company

Sahil Mattoo, a Senior Software Engineer at Eli Lilly and Company, is an accomplished professional with 14 years of experience in languages such as Java, Python, and JavaScript. Sahil has a strong foundation in system architecture, database management, and API integration. 

fullstack