Basics Of Thread Programming – Part 1

Introduction to Threads

 

            Thread programming is one of the advanced concepts but an essential one for making the software systems of today. Even though one is not aware of threading etc., threads are always there in the systems, as today’s operating systems are multithreaded and generally there will be at least one thread in each process. Operating Systems like Windows, Unix, and Linux etc all have thread support right from their core. Here is the very basic introduction of the threads

 

Thread is a flow of control or the flow of execution of the program. It is synonymous to the code/instructions that will be executed in a well defined order. For example it can imagined that a person as CPU capable of doing things intelligently. Each activity that person has to do can be thought as a thread of control. At a particular point of time there may be such threads of control that compete to get executed. It may be just like the person has to do two things at a particular point of time. Then he may divide his time among the two activities to complete them as soon as possible. In the similar way CPU completes both the activities by alternating the execution of the threads. In a time shared model it may run one thread for some time and then run the other thread for some time and then switch back to first thread and so on.

If two programs P1 and P2 are running on a system, then the code of P1 and P2 needs to be executed in a well defined order. But if the computer has only 1 CPU, that CPU can execute the instructions/code of only 1 program at a time, as the CPU can execute only 1 instruction at a time. But in a multiprocessing system both P1 and P2 needs to run at the same time. In this situation operating system divides its processing time for P1 and P2. For a particular time interval instructions/code of P1 will be executed and then it executes the instructions/code of P2. These time intervals are so small that it creates an illusion that both P1 and P2 are being executed simultaneously without any interruption.   This is called multi processing, i.e multiple processes being executed simultaneously. In real world situation many programs will be executing on the operating system.

 

 

 

            In a program there may be multiple tasks that are to be executed simultaneously (almost). For example there may be a chat application which has to receive the text sent to the current user and show/refresh the GUI where the user reads messages and writes his messages. In  such a situation code of each task will be executed on a separate thread and each thread will be executed for small intervals of time one after the other. GUI will be updated for some time and then the chat messages will be extracted for some time. Like this, in a single program tasks are executed  in time slices. For the tasks to be executed in this fashion, code is to be tied to threads or the threads should be given the code they should execute. In some environments threads are tied to the code implicitly(by the framework) like GUI threads in Java and in some environments this is to be done explicitly, like POSIX thread programming in C language where thread has to be told to execute a method whose pointer  has to be given.

 

·        How Threads can improve the efficiency?

1.      Utilize Idle Time Of Processes: When a task is being performed, CPU may not always be busy doing something for that process. There may be situations like waiting for user action like data input or waiting for some system event or data etc. These are quite normal situations in which the thread don’t have to wait all the time spending the precious CPU time. So other threads performing some other tasks can use the CPU time while the current thread can sleep for some time and later check if it has met the condition for performing it’s next task. This way the CPU cycles are not wasted.

2.      Synchronization Of Multiple Jobs: There can be some situations where the activities are to be performed synchronously, for example there can be 2  jobs J1 and J2 which are to be performed in a cooperative way. J1 after performing some task requests J2 to do some task for it and get back the result. For J2 to do processing, it’s thread gets into action to do the work and J1 goes to sleeping state as it can proceed till J2 gives the result of the task its performing. This way the cooperative jobs share the CPU for doing their work. Example can be data fetching and data visualization(with GUI etc.) jobs.

3.      Multiple Tasks Are To Be Performed Simultaneously: Sometimes many tasks are to be performed by the system or atleast such processing is simulated so that execution of on task wont wait till the other task is completed. Tasks may be executed simultaneously in time shared fashion so that each task will be executed along with others. Example is  a database server which serves the clients of huge number. In this case every clients request has to be processed on priority basis. So every request will be handled by a separate thread so that there lies the concurrent request processing for each client. Hence the client’s requests don’t have to be put into a very lengthy queue waiting for their chance.

4.      Background or Housekeeping Tasks: Sometimes there may be tasks that are to be performed from time to time and they cant force stopping the execution of normal tasks on a system. Such tasks will now and then get their chance of execution to perform their tasks. Example can be operating system jobs, system monitors etc which run in background not disturbing the normal processing in the system.

5.      Utilize The Multiple Processors: On multi processor systems, efficiency will be greater if multiple jobs are executed on different processors rather than executing a single job on different processors. In this case a single program may be executed on different processors with different threads of execution, or different programs may be executing on those CPUs with their own threads of execution.

 

Threading is not a solution for every performance related issue or requirement. Rather threading is an optimum solution to effectively utilize the CPU time for different tasks which hardly can wait to be performed one after the other. So sharing happens in two circumstances. First is when a thread don’t need the CPU time at any moment, some other thread will use that time. Second is concurrent execution of jobs is required badly. In this case these jobs share the CPU time in intervals.

 

·        Where is the Thread Programming required?

Thread Programming is programming the systems with thread aware or thread-based functionality. As told before threads are there everywhere in the modern day Operating Systems. So knowing about threads makes the life easy to avoid the potential problems and to improve the efficiency of the systems.

Generally another alternative for concurrent execution of tasks is multi processing (especially on Unix and Linux). But processes have more overhead when compared to threads, because of their housekeeping, limitations etc. Again processes have relatively strong boundaries around them, which makes the synchronization of tasks very difficult. Even the communication between two processes is complicated like inter process communication. Hence threads are good choice if some related tasks are to be performed concurrently to achieve a common goal. For performing such tasks thread programming is required.

But there are situations where the runtime environment of a process provides threads implicitly. In such an environment if only one thread is there then there is nothing to worry about threads. Anywhere if there is only one thread of execution in a process, there is nothing to care about thread programming. But life may not be that simple. In many situations having multiple threads in a process may be requirement rather than an option. For example a GUI framework may be showing GUI in one thread and handling the GUI events on different thread. Or in a server application, every client request may be processed on a separate thread. In such a situation thread programming is required to avoid the thread conflicts while accessing the same piece of code or data. Such conflicts are potential threats for the safe execution of the tasks.

Also when there are multiple threads of execution then there the synchronization may be required so that the threads work cooperatively for performing the tasks. Thread synchronization is also required for avoiding the threads conflicting for the access of code and data. This is called thread safe programming.

Rule of thumb is, thread programming is not required if there is only a single thread of execution in a program. If multiple threads of execution are there then at least the thread synchronization and thread safety programming is required. There the thread creation and management programming may also have to be written.

 

·        How does Operating System support Threads?

Generally threading is an abstraction provided by the operating system by time slicing the CPU time or executing different instructions on different CPUs. If T1 and T2 are two threads with their own code to execute, then operating system will execute the code of T1 for some time and it will switch to execute the code of thread T2 and so on. The smallest time unit for which the CPU executes the code of a particular thread is called time slice. But this switching of thread involves saving the state of T1 thread so that it can switch back to T1’s execution later to continue the processing. So the threads execution state like the register values, program counter(which instruction is the last executed) etc will be stored somewhere. Later when T1 gets its chance to be executed, then it will get back the state of thread execution and it will continue where it has stopped.

But typical operating system’s activities may not be as simple as T1 and T2 scenario discussed above.  There can be many processes running with each process having any number of threads.  So it has to manage a lot of threads in an efficient manner. There comes the scheduler  that schedules the thread execution. Scheduler is like a manager that assigns the time slices for thread execution. Each thread will have a priority level to specify how often the thread should get a chance to be executed. Scheduler will have a policy for handling the priorities of time slice allocation. It will decide which thread should be executed in the next time slice and for how long so that the threads with more priority will get more execution time in proportion to it’s priority. This is a generic situation. Now lets see some more details of scheduling.

Consider two threads T1 and T2 with their own code. If T1 is working a very lengthy task and it is running continuously then T2 will keep waiting for a very long time to execute it’s code. It is called that T2 is starving. In this context operating systems has 2 ways of handling threads. First is non-preemptive multithreading, in which T2 will get the time slice only when  T1 stops executing by itself. So naturally other threads keep starving for much time. The second is preemptive multithreading in which operating system will let T1 execute till it reaches the threshold time. Then it will force T1 to stop and T2 will get the chance to be executed. Hence thread starving will be minimum in this case. But operating system should do more work to keep track of the time slices of each thread and force the threads to stop the execution to give chance for other threads.

 

·        How different Operating Systems support for Threads?

Even though different operating systems offer the support for threads in a generic way there are many implementation differences. Most of the concepts of the threading apply commonly to the operating systems like Windows, Unix , Linux etc. But policies, parameters and the implementation details may differ a lot. Only the APIs and underlying implementations change across them. Windows operating systems offer the threading through win 32 APIs. This API support a lot of features for thread manipulation and thread safety.  Threading model of most of the UNIX flavors is a matured one and it follows the POSIX threading standard. Threading is quite efficient and feature rich on UNIX systems. Linux has got some sort of loose ends regarding threading . For example there is confusion about signals in the implementation aspect of threading. Linux also has adopted the POSIX standard for threading. There are many libraries available for Linux threading where some of them take support of kernel and some wont.

Apart from these the threading in Java is feature rich and Java qualify as a complete platform for writing platform independent (almost) applications irrespective of the differences in the threading support of the underlying Operating System. Java offers threading in a very user friendly and clean way. Java’s threading is purely dependent on underlying operating system’s implementation of threading, i.e. java offers the wrappers for  operating system’s implementation of threading. Hence the thread priorities are mapped loosely to the operating system’s priorities. For example Windows NT and Solaris has a very different models of thread priorities. There are other minor issues and problems related to the threading support.

 

·        Roadmap of this Tutorial

          This tutorial will introduce the basic terminology or Jargon of threads. After that comes the basic thread programming techniques in POSIX,  MFC ad Java. It shows the life cycle and management of threads, synchronization techniques etc. After that comes the thread programming patterns that show the thread usage patters for different situations and problems.