../
Looking for a senior C++ dev? I'm looking for work. Hire me!

Summary

I was first introduced to threads back in the early OS/2 days. From there, I've used OS-level threads and libraries in OS/2, Windows, and Linux. But I must admit, the new C++ standard has me excited. The fact that C++0x (aka C++11) is standardizing threading from a language point of view is a great thing for C++ developers. There are many books, blogs, and web sites discussing both general and specific threading topics, so I wont comment on those aspects. Instead, this article will focus on using std::thread for the first time.

GCC and G++

As C++0x/C++11 is increasingly adopted and supported by various compilers, I'm certain some details will change. This article is based on what was generally available in June/July 2011 on Ubuntu 11.04:

$ g++ --version g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2

Compiling

There are 2 tricks to using std::thread. The first tells gcc/g++ to enable the new C++0x features, the second is specific to std::thread and tells g++ to use pthread. It seems that std::thread is built on top of -- or relies upon? -- parts of the pthread library. Here is an example showing the exact flags required to compile an application which uses std::thread:

g++ -std=c++0x -pthread example1.cpp

There is also a -std=gnu++0x parm to g++ which apparently defines some GNU extensions to C++0x, but I haven't yet come across a document explaining the details of those extensions.

Pthread

Under the covers, GCC's implementation of std::thread is definitely still using pthread. This can be confirmed by setting a breakpoint in the thread, and examining the call stack:

(gdb) bt #0 doSomeWork () at example1.cpp:6 #1 0x000000000040212c in _Impl::_M_run (this=0x605030) at /usr/include/c++/4.5/thread:119 #2 0x00007ffff7b885f8 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #3 0x00007ffff7423d8c in start_thread (arg=0x7ffff7088700) at pthread_create.c:304 #4 0x00007ffff716f04d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112 #5 0x0000000000000000 in ?? ()
(See stack frame #3, which is a call to start_thread() in pthread_create.c.)

Default constructor

The default empty constructor for std::thread doesn't actually create a new thread. For example, this code will still have only the main thread:

std::thread my_array_of_threads[50]; // does not create any new threads

Only when a thread is told what code to run is a thread actually created:

void doWorkOnThread(void) { ... } int main( int argc, char *argv[] ) { std::thread my_array_of_threads[50]; // does not create any new threads my_array_of_threads[8] = doWorkOnThread; // immediately create a new thread and run this function ...

Joining and detaching

Using Valgrind, we can see that every thread we fail to join() or detach() leaks 288 bytes of memory:

==28865== HEAP SUMMARY: ==28865== in use at exit: 288 bytes in 1 blocks ==28865== total heap usage: 2 allocs, 1 frees, 360 bytes allocated ==28865== ==28865== 288 bytes in 1 blocks are possibly lost in loss record 1 of 1 ==28865== at 0x4C279FC: calloc (vg_replace_malloc.c:467) ==28865== by 0x4011868: _dl_allocate_tls (dl-tls.c:300) ==28865== by 0x55D7871: pthread_create@@GLIBC_2.2.5 (allocatestack.c:570) ==28865== by 0x4EE18CF: std::thread::_M_start_thread(std::shared_ptr) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.14) ==28865== by 0x401219: std::thread::thread(void (&&&)(), ) (in 2011-06-27_StdThread/a.out) ==28865== by 0x400EE2: main (2011-07-16_leak.cpp:29)

To prevent this leak, make sure all threads are properly joined or detached.

Signals

signals

Debugging

debugging

Exiting

exiting the main thread, exiting threads, calling things like exit() from threads

Static variables

Static variables are allocated from the heap, and thus are shared across all threads. Making a variable static does not prevent concurency problems. For example, with the following example code, the variable foo does not restart at the value zero for each thread that calls the function:

void fooFunc( void ) { static int foo = 0; foo ++; std::cout << "Foo is now: " << foo << std::endl; return; }

Instead, see the new thread_local keyword to define thread local storage:

void fooFunc( void ) { static thread_local int foo = 0; ...
The version of g++ I'm using (4.5.2) doesn't yet support thread_local so I had to revert back to the old extension __thread.

Differences between std::thread and boost::thread

differences between std::thread and boost::thread

Additional reading

Last modified: 2011-12-31
Stéphane Charette, stephanecharette@gmail.com
../