Rajinder Yadav - C++ Windows Development Tools & Resources :Design, Code, Test, Debug

Thread Concepts, by Rajinder Yadav
March 5, 2002

Introduction

Before we can talk about threads, we have to understand what a process is. When most people hear the word process they think of a program or a running application, in the simplest terms that's what it is. The Webster dictionary define process as, "a series of actions or operations conducing to an end;" We can use this definition as a good starting point for our discussion on threads. If we say that a program is a process that starts somewhere, does a few things and ends, then we can say that a thread is something that carries out each of those steps or operations in the process.

        Process is Inert           Running Process

      +--------------------+    +--------------------+
      |    Process A1      |    |    Process A1      |
      |+------------------+|    |+------------------+|
      ||   Global Data    ||    ||   Global Data    ||
      |+------------------+|    |+--------|---------+|
      |+------------------+|    |+--------|---------+|
      ||      Heap        ||    ||      Heap        ||
      |+------------------+|    |+--------|---------+|
      |+------------------+|    |+--------|---------+|
      ||      Binary      ||    ||      Binary      ||
      ||     Executable   ||    ||     Executable   ||
      |+------------------+|    |+---|----------|---+|
      +--------------------+    |    |          |    |
                                |+---+---+  +---+---+|
                                ||Thread1|  |Thread2||
                                |+-------+  +-------+|
                                || Stack |  | Stack ||
                                |+-------+  +-------+|
                                || Reg   |  |  Reg  ||
                                |+-------+  +-------+|
                                ||  IP   |  |  IP   || 
                                |+-------+  +-------+|
                                +--------------------+

So a process is a running program and a thread? Well not exactly, a process is a set of rules and statements(instructions) in the program. By itself, a process is inert which contain resources (as you can see in the above diagram).

A thread is what actually carries out (executes) those instructions to make the program run, each thread has it's own stack frame, which implies that each thread has it own copy of automatic (local) data values. All threads in a given process share the global data segment and the heap. All running process start off with a single main thread, the main thread in turn can spawn off additional threads to help it get more tasks done at the same time.

When programmers first learn about threading, they all go "ape" and start writing lots and lots of program using threads. Imagine all the works that could be done with a program having many threads of execution! To their dismay, they soon realize that writing multi-threaded application is not all that easy. It takes a lot of careful planning and thinking to make a multi-threaded application; coordinating all those thread to cooperate and not step on each other's toes can be challenging, just ask anyone who has spent weeks debugging a multi-thread program.

A little know truth that's not talked about, a badly written or designed multi-threads program can actually end up running much slower than one written with a single thread. There are many manifestation that can cause this to happen, we will talk about them in turn.

The first has to do with resources, the less resource you have to work with, the more poorly a process will function. What are these resource, well the most prominent ones are memory, disk, network, and CPU. All threads fight for the CPU, if you got an expensive calculation in the happening, it might take a while for the thread that updates the screen to get some CPU cycles (this may or may not annoy the user). Many threads can also end up trashing and fragmenting memory causing the memory manager to take longer and longer to find those free pockets of memory!

If many threads are fighting for a scarce resource then they will not be able to carry out their functions until the resource becomes free. If many threads need to attain control of more than one shared resource then the problem can get really critical, the most fatal outcome of this ordeal is called 'deadlock'. This occurs when a thread is waiting on a shared resource that never become available, and all it takes is two threads to cause a deadlock to occur.

Image that you have two boxes a red one and a blue one. One box is empty and the other box has a white ball inside it. Now imagine your boss has given you the assignment of writing a program that has two threads that do nothing more than grab each box, look inside them, swap the ball from one box to the other, and then put the boxes away and go to sleep for some random amount of time, only to wake up and repeat the steps. So you happily go off and write the code, you main function looks something like this

   public void run() {
      while(true) {
         pullBox();
         pullBox();
         swapBall();
         pushBox();
         pushBox();         
         sleep(getRandomTime());
      }
   }
But when you run the program, it's seem to run for a while and then hangs. You ask why? If you take a look at your program, you always assume a thread will get the two boxes, but if one thread has the red box, and the other thread has the blue box then both are deadlocked waiting for the other box to become free, each thread is unable to perform the swap indefinitely.

The other condition is known as 'racing' and is just as bad as the first. When two threads use a shared region of memory, the result can lead to data corruption or data loss. Data corruption can take place where two thread use a shared buffer to transfer data. Suppose one thread reads data coming over the network in 'chucks' and writes it to the buffer and the other thread reads the data from the buffer and writes it out to a file. If the two threads are not synchronize then we have a racing condition

File  Thread1  buffer  Thread2  Network        Time
                          read-> ---- <- data    t0  | 
        read->  ----   <-write                   t1  |
-     <-write             read-> ----- <- data   t2  v
--    <-write   ----   <-write                   t3  |
---   <-write             read-> ----- <- data   t4  |
----  <-write   xxxx   <-write                   t5  v
If you look at the diagram, you can see that Thread2 writes the data into the buffer as soon as it gets it, but Thread1 take a little longer writing the data to file. It takes Thread1 four cycles, from time t2 to t3 to write out the first data to the files. At time t3 the buffer is full with then next data waiting to be written to file. At time t4 more data arrives, and at time t5 data is written to the buffer causing the previous unsaved data to be overwritten (shown as xxxx).

If one thread is waiting for an operation to complete before it can continue, we say that this thread is 'blocked', in the diagram above, Thread1 is blocked on the file write operation during t2 to t5.

Copyright © 2002 Rajinder Yadav, All Rights Reserved

Home