Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

The std::thread Class

Last updated Jan 1, 2003.

The C++09 Committee Draft introduces the std::thread class which represents a thread of execution. Here I will explain the general differences between processes and threads, and show how to use std::thread to launch threads.

Processes versus Threads

Technically, a thread is a stream of instructions that the operating system can schedule to run independently. The operating system treats threads and processes as runnable entities which are collectively called tasks. However, threads and process differ in one crucial aspect -- creation overhead. The creation of a new process incurs a significant amount of overhead, even in modern operating system. In the POSIX world for example, a process consists of information about the program's resources and its execution state, including:

  • Various IDs (process ID, process group ID, user ID, and group ID)
  • Program instructions
  • Environment
  • Working directory
  • Registers
  • Stack
  • Heap
  • File descriptors
  • Signal handlers
  • Shared libraries
  • Inter-process communication facilities (message queues, pipes, semaphores and shared memory)

The creation of each new process is therefore a costly operation. As opposed to a full-blown process, a thread creation is associated with much less overhead. Threads duplicate only the bare essential resources that enable them to exist as executable code. Each thread maintains its own:

  • Stack pointer
  • Registers
  • Scheduling properties (such as policy or priority)
  • Set of pending and blocked signals
  • Thread specific data (objects with automatic storage and objects with thread-local storage)

While the new threading API of C++ is platform neutral, the general model of C++09 threads is quite similar to that of POSIX. Let's take a look at the C++09 std::thread class.

Creating a Thread

std::thread provides a simple and efficient mechanism for creating a new thread of execution, waiting for a thread to complete, and querying the state of a thread.

You launch a new thread by instantiating a std::thread object initialized with a function:

#include <thread> //C++09 header
void pwdcheck();
std::thread thr(pwdcheck);

When the thread object thr is created it's initialized with the address of the function pwdcheck(). That function is implicitly called in the newly-launched thread. When pwdcheck() returns, the thread terminates.

You can pass any callable entity to the thread, not just a function. The following example passes a function object as an argument to the thread:

class pwdcheck
{
public:
 void operator()();
};
pwdcheck pc;
std::thread thr(pc);

Notice that thr gets a copy of the function object pc. If creating copies isn't desirable, you can wrap the arguments with a reference wrapper:

pwdcheck pc;
std::thread thr(std::ref(pc));

Passing Arguments to the Thread Function

In older threading frameworks such as pthreads, the only way to pass arguments to the thread function is to pack the arguments in a struct and pass the address of that struct to the thread function. It's time you forgot about this kludge. With std::thread you can pass any number of arguments to the thread function directly. In the following example, the thread object is initialized with four arguments -- the thread function followed by its three arguments:

void pwdcheck(string *user, string *encryptedpwd, bool casesensitive=false);
std::thread thr(pwdcheck, "User1", "*$#(%95", true);

Thus far, the examples you've seen launch a new thread upon initialization. However, in some cases you want to create thread object without launching it yet. This can be useful when you want to create a pool of empty threads or when you want to move threads. You can move an active thread into an empty thread like this:

std::thread thr(pwdcheck, "User1", "*$#(%95", true);
std::thread thr2; 
thr2=std::move(thr); //thr2 becomes active, thr becomes "empty"

Alternatively, you can call swap():

std::swap(thr,thr2);

Notice that copying threads isn't allowed, nor does it make any sense.

Joining and Detaching

Once your process has launched a thread, it can wait for that thread to finish. This is called joining with the thread. To join with a thread, call the join() member function:

std::thread thr(pwdcheck);
thr.join();

If you don't want to join with the thread, you can destroy the thread explicitly by calling detach():

std::thread thr(pwdcheck);
thr.detach();

The operating system assigns a unique id for every thread in a process. You can use that id for comparing and sorting threads, storing threads in an associative container etc. To obtain the id of an existing thread, call the helper function get_id(). A thread id is stored in a variable of type thread::id which is a platform-defined integral type:

thread::id sp_id;
sp_id=thr.get_id();

Protecting Data

In a multithreaded application, you often need to protect shared data from simultaneous access by two threads. A mutex provides exclusive ownership for a thread. The new header <mutex> defines four mutex types:

  • std::mutex
  • std::recursive_mutex
  • std::timed_mutex
  • std::recursive_timed_mutex

The properties of each of these mutex types will be discussed in a separate article. I will use here only the simplest mutex type -- std::mutex.

Although all mutexes provide member functions for locking and unlocking a mutex, the more common approach is to use the std::unique_lock() and std::lock_guard templates instead. Following the RAII idiom, these templates lock a resource upon initialization and release it when they are destroyed. In the following example, lock_guard is used to gain exclusive access to str:

std::mutex m;
std::string str;

void func()
{
 std::lock_guard<std::mutex> lck(m); 
 process_string(str);
} // the mutex is released here