Part I built two grand illusions: many virtual CPUs, and a private address space apiece. The new abstraction lives inside one process: the thread thread A function running within the same memory space as other functions, with more than one active at a time. defined in ch. 2 โ open in glossary โ more than one point of execution, multiple PCs fetching and executing, all within one shared address space.
Each thread looks remarkably like a process: its own PC, its own private registers; switching between threads means saving oneโs state and restoring anotherโs โ into a thread control block (TCB) thread control block The TCB: per-thread saved state (PC, registers) inside a process โ the PCB's smaller sibling. Switching threads saves/restores TCBs but keeps the SAME page table, since the address space is shared. defined in ch. 26 โ open in glossary rather than a PCB. One glorious difference: the address space stays put, so no page-table switch is needed. And one structural change โ the stack multiplies:
click a region marked โ for details
click a region marked โ for details
Anything stack-allocated lands on the relevant threadโs stack โ thread-local storage thread-local storage Each thread's own stack โ where its locals, arguments, and return values live. A multi-threaded address space holds one stack per thread, scattered through the space (ruining the tidy two-ends layout, mostly harmlessly). defined in ch. 26 โ open in glossary .
26.1 Why Use Threads?
Two major reasons. Parallelism: with multiple CPUs and a big array to process, a thread per CPU turns one plodding pass into several concurrent ones โ parallelization parallelization Transforming a single-threaded program to spread its work across CPUs โ typically one thread per processor โ for real speedups on real hardware. defined in ch. 26 โ open in glossary , the standard route to speed on modern hardware. Avoiding blocked progress: while one thread waits on slow I/O (a message, a disk request, a page fault), the scheduler runs your other threads โ overlap of I/O and computation within a single program, exactly what multiprogramming achieved across programs. Hence thread-hungry web servers and DBMSs.
Could processes do both? Sure โ but threads share the address space, making data sharing natural; processes remain the sounder choice for logically separate tasks that shouldnโt share.
26.2 An Example: Thread Creation
The humblest possible threaded program โ main creates two threads that print โAโ and โBโ, then waits for both:
void *mythread(void *arg) {
printf("%s\n", (char *) arg);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p1, p2;
printf("main: begin\n");
Pthread_create(&p1, NULL, mythread, "A");
Pthread_create(&p2, NULL, mythread, "B");
// join waits for the threads to finish
Pthread_join(p1, NULL);
Pthread_join(p2, NULL);
printf("main: end\n");
return 0;
}
Three threads in all: main, T1, T2. Now โ in what order do things happen? Step through all three answers:
main: starts running; prints "main: begin"
Thread creation is like a function call that doesnโt return to you first: the system creates a new execution stream for the routine, and it runs independently โ perhaps before create() returns, perhaps much later. What runs next is the schedulerโs business, and while it presumably implements something sensible, it is hard to know what will run at any moment.
As you might also be able to tell: threads make life complicated. Computers are hard enough to understand without concurrency. With it โ as the next section demonstrates on a two-line loop โ it gets worse. Much worse.
Check yourself
1.A thread is 'very much like a separate process, except for one difference.' Which โ and what does it change about context switches?
2.What does the multi-threaded address space (Figure 26.1, right) do to the tidy single-stack layout?
3.The two major reasons to use threads:
4.In t0.c, main creates T1 ("A") then T2 ("B"), then joins both. Can "B" print before "A"?
5.When would you pick PROCESSES over threads for concurrent work?