The Big Picture: The Boss and the Helpers

When you open a program, Windows creates the process and one thread to start it. That first thread is the boss. As the program runs, the boss can hire helpers to do extra jobs. Helpers can also hire more helpers. When a job is done, the helper leaves.

 Time ───────────────────────────────────────────────►

 Main thread   ████████████████████████████████████  (lives as long as the program runs)
 Worker 1            ██████████                      (download a file)
 Worker 2                  ████████████              (save to disk)
 Worker 3                                ██████      (scan for spelling)

2.1 The Main Thread (Created at Process Start)

What it is: The main thread (also called the primary thread) is the first thread of a process. Windows creates it at the same moment it creates the process, when you call CreateProcess (the Windows function that starts a program; see Chapter 9).

What the main thread does:

  1. Starts at the program's entry point (the first instruction the program runs). For a C or C++ program, the C runtime (a small helper library) runs first, then calls your main or WinMain function.
  2. Runs your program's setup code.
  3. For a program with windows, it runs the message loop (see below).
  4. When it returns from main, the C runtime calls ExitProcess, which ends the whole process and all other threads.

This last point surprises many beginners. The main thread finishing normally means the program is over, even if worker threads are still busy.

The UI thread. In a program with windows (Notepad, a browser, File Explorer), the main thread is usually the UI thread. It owns the windows. It sits in a loop that does this over and over:

   ┌─────────────────────────────────────────────┐
   │  1. wait for a message (click, key, paint)  │
   │  2. handle it (run the code for that button)│
   │  3. go back to step 1                       │
   └─────────────────────────────────────────────┘

This is the message loop. While the UI thread is busy with one long job, it cannot read the next message. Windows notices after about 5 seconds and shows "(Not Responding)" in the title bar. The fix is to move long jobs to a worker thread (next topic).

2.2 Worker Threads (Created During Execution)

What it is: A worker thread is any thread that a program starts after the main thread, usually to do one specific job in the background.

Common worker jobs:

JobWhy a worker
Download or upload a fileWaiting on the network should not freeze the window
Read or write a big fileDisk is slow compared to the CPU
Heavy maths (video, zip, image filters)Can be split across cores
Wait for incoming connectionsA server needs to listen all the time
Autosave and spell-checkQuiet jobs in the background

Rules of thumb:

  • Worker threads share the heap with the main thread, so they can read the same data. But they must take care (Section 7).
  • A worker should not touch windows directly. In Windows, only the thread that created a window may safely change it. A worker sends a message to the UI thread instead ("please update the progress bar").
  • More threads is not always faster. If you have 8 logical processors, 50 busy threads just fight for them and add switching cost (Section 6).

Two kinds of work:

TypeMeaningBest number of threads
CPU-boundMostly calculatingAbout the number of logical processors
I/O-boundMostly waiting for disk or networkCan be many more, since most are asleep

2.3 Thread Creation API

What it is: An API (Application Programming Interface) is a set of functions the OS offers to programs. Windows gives programs several ways to create a thread.

FunctionLevelNotes
CreateThreadWin32 (the basic Windows API)The standard call. You give it a function to run, and it returns a handle and a thread ID.
_beginthreadexC runtimeUse this in C/C++ programs. It sets up the runtime's per-thread data and then calls CreateThread.
std::threadC++ standard libraryPortable. On Windows it uses the calls above.
System.Threading.Thread, Task.NETManaged languages (C#). Task usually uses the thread pool (Section 9).
CreateRemoteThreadWin32Starts a thread inside another process. Needs rights; used by debuggers and, sadly, by malware.
Thread pool functionsWin32The OS lends you a ready-made thread (Section 9).

A simple C example (just to read, not to memorise):

DWORD WINAPI Worker(LPVOID param) {
    // this code runs on the new thread
    return 0;               // thread ends with exit code 0
}
 
int main() {
    DWORD tid;
    HANDLE h = CreateThread(NULL, 0, Worker, NULL, 0, &tid);
    WaitForSingleObject(h, INFINITE);   // main waits for the worker to finish
    CloseHandle(h);                     // give the handle back to Windows
    return 0;
}

What happens inside CreateThread (simplified):

 1. Windows reserves memory for the new thread's STACK
    (the default reserve is about 1 MB; only a small part is used at first).
 2. It builds a kernel object that stores the thread's registers,
    ID, priority and state.
 3. It sets the instruction pointer to your function.
 4. It puts the thread in the READY state (Section 4)
    so the scheduler can run it soon.

Creating a thread is much lighter than a process, but it still costs thousands of CPU instructions plus memory. That is why programs that create lots of short-lived threads use a thread pool instead.

Parameters worth knowing:

ParameterMeaning
Stack sizeHow much stack to reserve. 0 means "use the default".
Creation flagsCREATE_SUSPENDED starts the thread paused, so you can set things up first
Thread IDA number Windows gives back to identify the thread

2.4 Thread Termination

What it is: A thread ends in one of these ways.

WayWhat happensGood or bad?
Returns from its functionThe thread ends and its exit code is savedBest. Clean.
ExitThreadThe thread asks Windows to end itFine
TerminateThreadAnother thread kills it instantlyDangerous. The victim gets no chance to clean up. It may hold a lock forever or leave memory half-changed. Avoid it.
Process ends (ExitProcess, crash, or killed in Task Manager)All threads endNormal at the end of a program

Safe way to stop a worker: use a stop signal. The worker checks a flag or an event (Section 7) every so often. When the flag is set, it finishes its current step, cleans up and returns.

 main thread:  "please stop"  ──► sets stop-event
 worker:       ... work ... check event ... work ... check event → set!
               ──► clean up ──► return        ✔ safe

After a thread ends:

  • Its stack memory is released.
  • Its kernel object stays until every handle to it is closed (call CloseHandle). This is how another thread can still read the exit code with GetExitCodeThread.
  • A thread that is waiting on this one (WaitForSingleObject(h, ...)) is woken up.
  • The thread state becomes Terminated (Section 4).

Handle leaks: If a program creates threads but never closes the handles, the kernel objects pile up. Task Manager's Handles column slowly grows. This is a classic bug.

2.5 Thread Count Limits

A process cannot have unlimited threads. Each one needs a stack (reserved address space) and kernel memory. A 32-bit process has only 2 GB of address space by default, so thousands of threads with 1 MB stacks can run it out. A 64-bit process has far more room, but each thread still costs memory and switching time. The real limit is almost always good design, not the OS.

Summary

TermPlain meaning
Main (primary) threadThe first thread, created with the process
UI threadThe main thread of a window program; runs the message loop
"Not Responding"The UI thread was too busy to read messages
Worker threadA helper thread started later for one job
CreateThreadThe basic Windows call that makes a thread
_beginthreadexThe C/C++ way; wraps CreateThread
CREATE_SUSPENDEDStart the thread paused
Return / ExitThreadClean ways to end a thread
TerminateThreadForced kill. Dangerous. Avoid.
Stop signalA flag or event that asks a worker to finish politely