In this section, I am giving you a brief idea about threads and how it differs from a process. Also I am explaining its advantages over a process. In my previous section I have given a brief idea of process and you can refer that section with the linkReference to a process
Let me first begin with the definition of a thread. What is a thread? Threads are light weighted processes that can share an address space and they have less overhead. But a process is an abstraction of a stand-alone computer. They don’t share memory but communicate with one another by messages. Two processes can run at the same time (both in pseudo-parallel and in true parallel on a two-processor machine) but they do not share memory. But if we provide software entities that can run at the same time sharing the same memory, such entities are called threads. Threads exist in many newer operating systems, and have some of the properties of a process. The figure shows two processes, one with three threads and one with four threads.
A thread can execute anywhere in the code space of the process. They even execute exactly the same code at same or at different times, but by fetching and executing the same instructions with different registers and stack. To make it more clear, let me extend our operating system concepts to implement threads. The operating system still provides processes which may further create new processes. But along with this new threads can also be created and in anynumber. All processes have an associated address space in which it runs, and a thread will have an associated process in whose address space it runs. When a process is created, an initial thread may be created in it which can further create new threads in the same process.These threads can exit at any time, and the process will exit only when the last thread in theprocess exits. Each thread will have its own stack and its own set of registers which are nevershared by other threads.
Thread system calls – Operating system allows programmer to create new threads if needed and only 3 system calls are needed for complete processes.
int CreateNewThread( char *startNewAddress, char *new_stackBegin ); - When this line of code is executed a new thread is created that starts executing at address startNewAddress andhas a stack at new_stackBegin. The return value is the thread identifier of the newly startedthread and it will be an integer.
int WaitPreviousThread( int thread_id ); - This system call causes the thread making the system call to wait until the thread specified by thread_id exits. The value returned is the return code from the ExitThread call.
void ExitThread( int retCode ); - This system call causes the calling thread to exit. If this one is the last, then both the process as well as the thread exits. The retCode is the returned codereturned to the parent process when it executes the Wait system call. Otherwise it is passed tothe thread that calls WaitPreviousThread on it.
When a thread creates a new thread, it not only allocates stack space for it to use but also determines where it will start execution. ExitThread exits the thread and it exits the process if it is the last thread to exit. WaitThread system call waits for a process to exit. A process exits when its last thread exits. It is executed by a thread in one process to wait for another process or last thread in it to exit. We can notice that these calls are very much similar to the process calls. The only difference is that, when we create a process, a set of code or a program is given to it to run. OS itself finds memory for it, and the new processes are allocated space from that given allotted memory only. In a thread, we have to allocate new stack space and tell the CreateNewThread call where it is. Here no new program is loaded, but we pass an address through the current program, where the new thread can start to execute.
Advantages of threads over processes
Threads provide parallel or pseudo-parallel processing like processes, but more efficient than processes. They are cheaper to create and destroy since they do not require allocation or de-allocation of a new address space or other process resources such as sharing common memory stack or duplicating open files. So, we can use multiple threads much more freely and for doing a small task, like a few thousand instructions, can create a new thread too. Threads share memory and since they don’t use system calls to communicate they are very much faster than processes. They communicate through memory and hence best applicable for parallel activities that use the same data structure and are tightly coupled. It is faster to switch between threads than processes. Threads are often called a ‘lightweight process’ to emphasize the fact that a thread is somewhat like a process but more efficient and uses fewer resources comparing to a process, and hence is lighter.
How threads can be used efficiently?
Clearly an OS implements both processes and threads. It depends on the programmer's choice to choose parallelism at any level, the process level or at the thread level. Processes provide an upper level of parallelism for parallel tasks that do not interact heavily and do not need to share very much data. Threads provide the occurrence of parallel activities inside a single process.This level is best for parallel activities that do interact heavily with each other and share a lot of data. The concept is briefly explained below with the help of examples.
Threads made waiting for you:
We can use multiple threads in the same program itself, one for waiting for some device while other threads assigned to do some activities like computation and display of results. Thus at the same time itself, all the three processes can be done. For example, we can have one wait thread to calculate some formula, one for the I/O task to complete and another one for printing the output.
Threads to partition process activities as modules:
Let us take a spreadsheet program to display a large spreadsheet. We might have one thread to get input data, another one for updating the screen, one for re-calculating data and one more to execute commands. If user makes changes to a cell that causes a prolonged recalculation, the re-calculation thread will start processing. When the process is completed, it informs screen update thread to redisplay the cell. At the same time, the user can type another command while the other two processes are going on in the background, without having to wait till these two processes complete. It is illustrated in the figure given below.
Program is more responsive to the user, since user doesn't have to wait. It is easier to program the system with this type of organization, with separate activities assigned to different threads. It is efficient to wait for things with threads. A thread is only a part of a program. So, even if one thread is blocked, the rest of the threads will still be active. Still it has disadvantages. Like processes, threads also have the same problems of parallelism.
Threads to replicate activities:
A thread can be used in a server process. It receives requests, acts on them and does what is requested. For example, a disk block server will accept requests to read and write disk blocks. A server may have several requests pending at the same time, and it may want to do more than one function at a single time. Here each request may be assigned to separate threads. If a thread blocks (on disk I/O), then only that one thread is blocked and there will still be other threads to serve new requests. Figure shows how a disk server uses separate threads for each request.
How a thread can be implemented?
Since threads exist inside processes we can have processes in the operating system. But some of its tasks will be done by threads. So the main objectives will be to redistribute these functions and assigning proper tasks. Here also, we have a process table and a process descriptor, but contain less information than before. Memory is allocated to a process. So the process descriptor will record the base and bound values for the memory area allocated to the process. Each process will have one or more threads associated with it. Hence the process descriptor will contain the head of the list of threads associated with this process. These thread descriptors will be included in a thread table which also include a register save area because the threads are dispatched and interrupted and hence it will need a memory space to save their processor state. The thread descriptor also contains the pid of the process it belongs to, pointers so it can put on a list of threads, and a thread state. The dispatcher will look in the thread table for a thread that is not blocked and dispatch it. These thread descriptors will be included in a thread table which also include a register save area because the threads are dispatched and interrupted and hence it will need a memory space to save their processor state. The thread descriptor also contains the process_id of the process it belongs to, pointers and a thread state. The dispatcher will look in the thread table for an unblocked thread and dispatches it.
New process concept
Earlier we defined a process as an entity having its own address space and it was allocated processor time once it starts running. But, by the introduction of threads, we split up the process concept into two parts. A thread became an entity that is scheduled by the dispatcher and receives processor time to execute. The (new) process became the entity that can request and hold resources according to need. Splitting the concept allows more flexibility. Now, we can have several threads in a single process and get the benefits of multiprocessing inside a process.
Lightweight processes and user threads
Threads are implemented by the operating system and require a change to the OS. The threads described here are usually called lightweight processes since they are cheaper version of processes. We can also implement threads at the user level without any assistance from the operating system. This technique is known as user threads. We just have to implement a little scheduler in a process that switches between threads, which does not require any memory mapping. It is basically a slight change in the registers and a jump instruction and the user process does not need to be in system mode to do this. User threads are much more efficient than lightweight processes because they do not require a switch to the operating system to change threads. But they are not recognized by the operating system and so cannot be used flexibly as kernel threads. It can be used to wait for messages on several message queues.
Examples of threads
Most modern operating systems (Windows NT, OS/2, Mach, UNIX and Solaris) implement lightweight processes. But System V Release 4 does not implement threads. Another exception is Plan 9.Most operating systems provide user thread packages that implement threads at user level.
Conclusion
We can conclude that threads are lightweight processes that share a common address space. They are more efficient than processes since they share memory and a process can have any number of threads. Each thread can be assigned a separate task, thus providing pseudo-parallelism and memory utilized in a better way. They are flexible, cheap and easy to create and destroy. Almost all latest operating systems implement threads.