CS 321 Spring 2013  >  Lecture Notes for Monday, February 4, 2013

CS 321 Spring 2013
Lecture Notes for Monday, February 4, 2013

Threads (cont’d) [2.2]

Introduction to Threads

What are Threads?

Threads are like processes, but share a common memory space. For efficiency reasons, each thread is typically not managed by the kernel as a separate process.

Why Use Threads?

Threads allow us to do multiple, concurrent operations involving a common dataset in a way that is efficient and convenient. Doing the operations sequentially could result in some operations being excessively delayed. Switching between operations in our application is likely to be painful to write.

For example, in a web server, we could have one thread that takes requests for web pages. For each request, we could have a separate, temporary thread that manages the resulting connection and serves the responses.

Threads can be implemented in a “lightweight” manner, with less overhead than kernel processes. This allows threads to be created, terminated, and switched between very efficiently.

On a machine with multiple processors/cores, threads allow a single process to make use of more computing resources than single-thread code would.

A Thread Model

Threads could be implemented using the standard kernel process mechanism. It is only necessary to give processes access to the same memory. However, this makes creating, terminating, and switching threads as expensive as the same operations for processes. This defeats an important reason for using threads.

Therefore, threads are often implemented using a lighter-weight mechanism. We allow a kernel process to “own” multiple threads. Each thread needs to keep track of the following.

All other process-related information: address space, open files, accounting information, running userid, parent/child information, etc., is the same for all threads owned by a process, and so can be tracked by the process.

Since threads need to keep track of so little information—essentially a few registers and a stack pointer—it is possible to switch between threads quickly.


Making a new thread is called spawning. One thread spawns another with access to the same address space.

Typically, there is one main thread (the master thread) that manages all the others (slaves).

Recall that, when a parent process forks a child, the parent often waits for the child to terminate. The corresponding operation for threads—usually the master waiting for a slave to terminate—is called joining. Explicitly letting a created thread continue without ever being joined is called detaching the thread.

A race condition is when scheduling decisions can affect the correctness of a program; these are common in poorly written multi-threaded code.

A data race occurs when two threads concurrently access the same data, one of them writes, and the other reads or writes. Despite its name, a data race may not be a race condition.

An operation is atomic if it is guaranteed not to be interspersed with other operations (for example, those performed by other threads), and it can never be half-completed.

C++11 Threads

Introduction & Compilation

The 2011 ANSI/ISO C++ standard includes a comprehensive threads package (the previous standard, published in 2003, had no thread support).

Most of the threads functionality is contained in the standard header <thread>.

Your compiler may need configuration before it handles C++11 threads correctly. For example, I am using g++ 4.7, and I had to add the following options to the command line: “-std=c++11 -pthread”.

Thread Objects

Support for a thread is provided by an object of type std::thread; each object encapsulates a different thread.

#include <thread>
using std::thread;

When we create a thread, we give it a function call to make. The thread makes the function call; then it terminates when the function returns. To create a thread, pass the constructor of std::thread the name of a function (or function object) to call, followed by the parameters to pass.

void foo(int k, double x);

Then later in the code:

thread t1(foo, 5, 3.6);

Note: Only by-value parameters work this way. By-reference parameters can be handled, but it is more complicated.

The above creates a new thread, encapsulated by object t1. This thread makes the function call foo(5, 3.6) and then terminates.

It is possible that the requested thread cannot be spawned (for example, the system may have a limit on the number of threads). In this case, the constructor of std::thread will throw an exception.

A default-constructed std::thread object does not encapsulated a thread. Use it later to create a thread by setting it equal to a thread contructor call.

thread t2;
t2 = thread(foo, 1, 2.2);

So, for example, we can create a vector of default-constructed threads and then use each to manage a thread as above.

The above code uses the move assignment of std::thread. But std::thread has no copy assignment, since threads cannot be copied. Thus, the following is illegal.

// t1, t2 are objects of type std::thread
t2 = t1;  // DOES NOT COMPILE!

If a thread object encapsulates a thread, then destroying the object terminates the thread.

Typically, when the process exits (system call sys_exit in *ix, or coming to the end of main), every thread terminates. So do not call exit in a slave thread.

We see that C++11 threads do not allow for starting up some task and then exiting, leaving the new task running; to do that, fork a child process.

Joining & Detaching

Every thread encapsulated by a thread object must be either joined or detached before its thread object is destroyed.

To join a slave thread, some other thread (usually the master) calls the thread object’s join member function. This takes no parameters.

// NOT in the thread encapsulated by t1

Similarly, to detach a slave thread, call the thread object’s detach member function, which also takes no parameters.

// NOT in the thread encapsulated by t2

See thread1.cpp (NetRun link) for a simple program using C++11 thread objects.

See thread2.cpp (NetRun link) for an example of thread objects stored in a vector and of handling thread-related exceptions.

CS 321 Spring 2013: Lecture Notes for Monday, February 4, 2013 / Updated: 4 Feb 2013 / Glenn G. Chappell / ggchappell@alaska.edu