Chapter 1. What are threads? Why use them?

In this chapter:

History.

In the early days of computing, all programming was essentially single threaded. You created your program by punching holes into cards or tape, submitted your deck of cards to the local computing centre, and after a few days, you received another deck of cards, containing, if you were lucky, the required results. All processing was batch, not time critical, first come first served, and when your program was running, it had exclusive use of the computer's time.

Things have moved on. The concept of multiple threads of execution first appeared with time sharing systems, where more than one person could be logged into a central mainframe computer at once. It was important to ensure that the processing time of the machine was fairly divided between all users, and the operating systems of the time made use of the "process" and "thread" concepts. Desktop computers have seen a similar progression. Early DOS and Windows systems were single tasking. Your program ran exclusively on the machine, or not at all. With increasingly sophisticated applications, and increasing demands on personal computers, especially with respect to high performance in the graphics and networking areas, multiprocess and multithread operating systems are now commonplace. Multithreading on PC's has mainly been driven by the need for better performance and usability

Definitions.

The first concept to define is that of the process. Most Windows 95, 98 and NT users have a good intuitive idea of what a process is. They see it as a program which runs on the machine, co-existing and sharing CPU, disk and memory resources with other programs. Programmers know a process to be an invocation of executable code, such that that code has a unique existence, and the instructions executed by that process are executed in an ordered manner. On the whole, processes execute in isolation. The resources they use (memory, disk, I/O, CPU time) are virtualised, such that every process has its own set of virtual resources, untouched by other processes. The operating system provides this virtualisation. Processes execute modules of code. These may be disjoint; in the sense that, the executable modules of code comprising Windows Explorer and Microsoft Word are disjoint. However, they may also be shared, as in the case of DLL's. The code for a DLL is typically being executed in the context of many different processes, often simultaneously. The execution of instructions is on the whole not ordered between processes: Microsoft word does not stop opening a document just because the print spooler is currently sending something to the printer! Of course, where different processes interact, the programmer must impose an ordering, a central problem which will be covered later.

Our next concept is that of the thread. Threads were developed when it became clear that it was desirable to have applications which performed sets of actions in a more loosely time ordered fashion, possibly performing several sets of actions at once. In situations where some actions would cause a considerable delay in one thread of execution (e.g.. waiting for the user to do something), it was often desirable to have the program still perform other actions concurrently (e.g. background spell checking, or processing incoming network messages). However, the overhead of creating a whole new process for each concurrent action, and then having the processes communicate was often far too much of an overhead.

An Example.

If one needs to look for a good example of multithreading, then Windows Explorer (i.e. the Windows Shell) is an excellent example. Double click on "My Computer", and click through a few sub folders, creating new windows as you go. Now invoke a lengthy copy operation on one of those windows. The progress bar pops up, and that particular window does not respond to user input. However, all the other windows are perfectly usable. Obviously, several things are going on at once, but only one copy of explorer.exe is running. This is the essence of multithreading.

Time slicing.

In most systems that support multithreading, there may be many users making simultaneous requests on the computer system. Normally the number of physical processors in the system is fewer than the number of threads that might be run in parallel. Most systems support time slicing, also known as pre-emptive multitasking, in order to get around this problem. In a system that is time sliced, threads run for a short while, and are then pre-empted; that is, a hardware timer fires which causes the operating system to re-evaluate which threads should be run, potentially stopping execution on currently running threads, and running other threads which have not be executed recently. This allows even single processor machines to run multiple threads. On PC's a timeslice tends to be about about fifty five milliseconds long.

Why use threads?

Threads should not alter the semantics of a program. They simply change the timing of operations. As a result, they are almost always used as an elegant solution to performance related problems. Here are some examples of situations where you might use threads: All of these examples have one thing in common: In the program, some operations incur a potentially large delay or CPU hogging, but this delay or CPU usage is unacceptable for other operations; they need to be serviced now. Of course there are other miscellaneous benefits, and here they are: The wise use of threads turns slow, clunky, not very responsive programs into crisply responsive, efficient, fast programs, and can radically simplify various performance and usability problems.

[Contents] [Next]

© Martin Harvey 2000.