Threads

What is a thread?

Why use threads?


What is a thread?

First let's see how the comp.programming.threads FAQ answers the question "What is a thread"?

"A thread is an encapsulation of the flow of control in a program. Most people are used to writing single-threaded programs - that is, programs that only execute one path through their code "at a time". Multithreaded programs may have several threads running through different code paths "simultaneously".

Every program that you wrote in 142, 143, and 373 had a single thread. This thread began at the beginning of main and continued to execute until your main function returned. This is known as a single-threaded application.

A multi-threaded application is a lot like having multiple functions in your program that all execute at the same time. (Because the CPU can only run one thread at a time, the threads don't actually execute simultaneously, but instead the CPU provides this illusion by switching between the threads very quickly.) In Windows, a program explicitly creates another thread by calling CreateThread. The most important argument to CreateThread is the function to begin executing.

CreateThread( Foo ) is different than calling Foo(). A call to Foo() will not return until Foo returns. CreateThread( Foo ) creates another thread that begins executing at the start of the Foo function. The CreateThread call will return before Foo finishes executing, and two threads will be active: the main thread and the Foo thread. The Foo thread continues to be an active thread until it returns.

Here is a simple program that creates two additional threads. One thread prints "Foo" and the other one prints  "Bar". The main thread starts the Foo thread and Bar thread and then sleeps for 5 seconds (i.e. 5000 ms). When the main thread exits, the entire process exits even though some of the other threads are still active. You can try out an executable version of this program here.

#include <windows.h>
#include <stdio.h>

unsigned long __stdcall 
Foo( void * x ) {
    while( 1 ) {
        printf( "Foo\n" );
    }
    return 0;
}
  
unsigned long __stdcall   
Bar( void * x ) {
    while( 1 ) {
        printf( "      Bar\n" );
    }
    return 0;
}

void
main() {
    HANDLE threadFoo, threadBar;
    DWORD idFoo, idBar;
    threadFoo = CreateThread( NULL, 0, Foo, NULL, 0, &idFoo );
    threadBar = CreateThread( NULL, 0, Bar, NULL, 0, &idBar );

    Sleep( 5000 );
}

The output of this program is:

Foo
Foo
...
Foo
         Bar
         Bar
         ...
         Bar
Foo
Foo
...
Foo
         Bar
         Bar
         ...
         Bar
   . . .

From the output, it's very apparent that the CPU is switching back and forth between the two threads. Another very important fact about threads is that they share the same address space. This means that when one thread changes the value of a variable, the other threads will see that change. We've changed the above program to have the user control when the threads stop. The two threads continue to print "Foo" or "Bar" until a global variable, stopPrinting, is set to TRUE. The main thread sets stopPrinting to TRUE after the user hits Enter, and at this point, the Foo thread and the Bar thread exit. The main thread uses the call WaitForSingleObject (described below) to wait for these two threads to exit. You can try out an executable version of this program here.

#include <windows.h>
#include <stdio.h>

BOOL stopPrinting = FALSE;

unsigned long __stdcall   
Foo( void * x ) {
    while( !stopPrinting ) {
        printf( "Foo\n" );
    }
	printf( "exiting from Foo\n" );
    return 0;
}
  

unsigned long __stdcall   
Bar( void * x ) {
    while( !stopPrinting ) {
        printf( "      Bar\n" );
    }
	printf( "exiting from Bar\n" );
    return 0;
}

int
main()
{
    HANDLE threadFoo, threadBar;
    DWORD idFoo, idBar;
    char c;    

    printf( "I'm about to start some threads.  Hit Enter to stop.\n" );
    Sleep( 5000 );
    
    threadFoo = CreateThread( NULL, 0, Foo, NULL, 0, &idFoo );
    threadBar = CreateThread( NULL, 0, Bar, NULL, 0, &idBar );

    scanf( "%c", &c);     // wait for the user to hit Enter
    stopPrinting = TRUE;

    //
    // The WaitForSingleObject call waits for something
    // to happen.  The calling thread essentially goes to sleep
    // until what it is waiting for happens.
    //
    // The first parameter is what we want to wait for.  In this
    // case, we want to wait for the threadFoo (and threadBar)
    // thread to finish. 
    //
    // The second parameter denotes how long we want to wait.
    // INFINITE means we want to wait forever.
    //
    WaitForSingleObject( threadFoo, INFINITE ); 
    WaitForSingleObject( threadBar, INFINITE );

    printf( "The threads have finished.  Press Enter to quit.\n" );
    scanf( "%c", &c);     // wait for the user to hit Enter 

    return 0;
}
The output of the above program is:
I'm about to start some threads.  Hit Enter to stop.
Foo
Foo
...
Foo
         Bar
         Bar
         ...
         Bar
Foo
Foo
...
Foo
         Bar
         Bar
         ...
         Bar
<User hits Enter>
exiting from Foo
exiting from Bar
The threads have finished.  Press Enter to quit.

Why use threads?

Increasing the performance of a program is sometimes a reason to use threads.  If the program is running on a computer with multiple CPUs, then each thread can run on a separate CPU, whereas a single-threaded program could only run on a single CPU. Or if a program is both I/O and CPU intensive, then making it multithreaded will allow it to do I/O at the same time as using the CPU, whereas a single-threaded program couldn't use the CPU while it is waiting for I/O.

A more common reason to use threads is because it makes it easier to break a program into smaller tasks that need to happen at the same time.   Take Microsoft Word for example. It does a lot of stuff all at the same time. A sample of things that are happening concurrently

You could do this with only one thread, but it would be ugly, it would be difficult to keep Word interactive, and it would harder to code. Here's an example of what a single-threaded solution would look like:

  word_main()
  {
    while( !exit ) {
      if the user has typed something, then show it on the screen
      if the last word was typed was a typo, then correct it
      if the last word was misspelled, then underline it
      if it looks like the user is making a list, then indent it
      if a document is being printing, then send the next few lines
        to the printer
      if auto-saving a document, then save the next part of the
        document
      make the paperclip blink
      ...
    }
  }

A much cleaner solution is to have each of these tasks handled by a separate thread.

  word_main()
  {
    CreateThread( DisplayUserInput );
    CreateThread( CorrectTypos );
    CreateThread( CheckSpelling );
    CreateThread( AutoFormat );
    CreateThread( AnimatePaperClip );
    ...
  }

Each of these threads runs sequentially without worrying about what the other threads were doing. (The PrintDocument and AutoSave threads would be created only when the user printed a document or when Word decided to save the document). In DOS and Windows 3.1, you can't print a document and edit the document at the same time because they are single-threaded operating systems, which is a big drag.

Not all Microsoft applications are multithreaded. Outlook, which is Microsoft's email client, is single-threaded. This means that you can't download new email messages and compose a new message at the same time, which is  a drag. I was told that Microsoft chose to make Outlook single-threaded because it would have been too expensive (by some metric, e.g. $$$ or time-to-market) to make it multi-threaded.


If the concept of threads is still unclear, then please ask me questions on Wednesday.