Skip to content

Investigating C++11 Threads

February 24, 2013

Introduction

I’ve been writing multi-threaded code, mostly for servers, for many years now, but I’ve always used native threads on Windows and Solaris, the portable pthreads library, or various commercial threading libraries, such as RogueWave. I’ve always had a somewhat sneering attitude to the C++ Standard Library threads, as I assumed they wouldn’t provide all the features I need, and would never be fully implemented. But recently I realised that this was a somewhat blinkered way of looking at things, and that maybe it was time to investigate the C++ library threads a bit more deeply.

What follows is how I wrote a very simple application to check out the C++ threads library features. It won’t contain any startling information  for people who already know the library, but may be of interest to those who don’t. You don’t need any great C++ expertise to follow it, but if you want to build the code you will need a fairly up-to-date C++ compiler. I used the excellent Windows MinGW-builds implementation of GCC which has become my compiler of choice recently (update: make sure you get the POSIX threads version if you want to work with std::thread).

A Simple Program

The program I chose to write was that beloved of all people writing threading examples. It simply prints out some numbers sequentially. Here’s the single-threaded version:

#include <iostream>
using namespace std;

void printnums( int start, int n ) {
    for( int i = 0; i < n; i++ ) {
        cout << "[" << start + i << "]" << endl;
    }
}

int main() {
    printnums( 0, 100 );
}

which produces the exciting output:

[0]
[1]
[2]
...
[99]

and is executed by the thread automatically created for you by the C++ runtime when you execute main(). So, how to run the printnum() code in a separate thread? Time to dive into the documentation. Although it is far from perfect, I recommend www.cppreference.com for this purpose.

Creating A Thread

Using the above reference, it seems that all I needed to do was to create  a std::thread  object, passing the thread’s constructor the name of the function I wanted to run, and its parameters:

#include <iostream>
#include <thread>
using namespace std;

void printnums( int start, int n ) {
    for( int i = 0; i < n; i++ ) {
        cout << "[" << start + i << "]" << endl;
    }
}

int main() {
    thread t1( printnums, 0, 100 );
}

This is a lot simpler than the native thread libraries, which are basically C APIs and require you to mess with void pointers and custom structs to pass the parameters to the function you want implemented. A big plus for the C++ library implementation!  Note that I could have also bound the parameters to the printnums() function myself, like this:

thread t1( bind( printnums, 0, 100 ) ); 

which is what the thread constructor is doing for me – for more info on std::bind, see this other blog article of mine.

I compiled this code with the MinGW-builds compiler like this:

g++ -std=c++11 -Wall -Wextra athread.cpp

The -std=c++11 option tells the compiler I’m using features from the C++11 Standard, and the other two options are my standard warning levels. Somewhat to my surprise, it  built with no errors, particularly no linker errors. I was surprised because my past experience with building any C++ code that uses pthreads (which this implementation of C++11 is using under the hood) normally requires all sorts of messing with explicitly telling the build system that you are using pthreads. Not so here – a big plus for the MinGW-builds implementation!

However, running the program was not such a happy experience. It did indeed print out all the numbers I expected, but then terminated with everyone’s favourite Windows dialog:

stoppedworking

Why would this be? Well, as a somewhat experienced threads programmer I actually knew the answer to this one. My program really had two threads – the one started for me automatically in main(), and the one I started explicitly via the std::thread constructor. The parent thread is the one in main, and if that thread exits before any child threads  it has created exit, then all bets are off, and you are in Undefined Behaviour Land. In this case, the program crashed – you might be less lucky, and have such a program appear to "work".

The fix for this is usually pretty simple – whatever threading library you are using will provide some function to allow the parent thread to wait for the child thread(s) to exit. Thread library writers love giving this function bad names – typically "join", because it "joins the two threads back together". Actually, it does no such thing, but that’s library writers for you.

So the fix for my program (after a quick check to see that the C++ library does use that bad name "join") was to re-write main() like this:

int main() {
    thread t1( printnums, 0, 100 );
    t1.join();
}

This made the main() thread wait until the t1 thread exits, and so all is well and I got a  nice list of numbers.

Two Explicitly Created Threads

I now had two threads running (one implicitly created, the other explicitly), so the next thing to try was creating creating another thread, which prints numbers starting at 1000 instead of zero:

int main() {
   thread t1( printnums, 0, 100 );
   thread t2( printnums, 1000, 100 );
   t1.join();
   t2.join();
}

This produced two streams of numbers in parallel  – one beginning at 0, and one at 1000. The streams were interleaved, but the output looked somewhat screwed up. On my system, it looked like this;

[[1000]0]
[1]
[2]
[3]
[4
[1001]

The reason for this is that I hadn’t serialised the access to the single std::cout object. The C++ Standard says that multiple threads may safely access C++ ostreams, but makes no guarantee that the output produced will be sensible. I needed to use a mutex to make sure that only one thread at a time was writing to cout – back to the documentation!

Mutexes

The Standard Library provides a variety of mutexes, declared in the header file <mutex>. A very simple one would do for my purposes, so I rewrote my code like this:

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

void outnum( int n ) {
    static mutex m;
    m.lock();
    cout << "[" << n << "]" << endl;
    m.unlock();
}

void printnums( int start, int n ) {
    for( int i = 0; i < n; i++ ) {
        outnum( start + i );
    }
}

int main() {
    thread t1( printnums, 0, 100 );
    thread t2( printnums, 1000, 100 );
    t1.join();
    t2.join();
}

The mutex needs to be static so that all threads that enter the outnum() function use the same mutex.

This code worked well; the output from the threads is no longer jumbled together. However, I was a bit worried by having to unlock the mutex manually. Whenever you find yourself doing this kind of thing, it’s best to create a helper class that will do the unlocking using RAII. That way, if the locked code ever throws an exception, the mutex will be automatically unlocked.

It turns out I didn’t need to write such a class myself – one is provided by the Standard Library. I simply needed to modify my code like this:

void outnum( int n ) {
    static mutex m;
    lock_guard <mutex> g( m );
    cout << "[" << n << "]" << endl;
}

This creates a lock_guard object who’s constructor calls lock() on the mutex. When the guard object goes out of scope (or an exception is thrown), the guard’s destructor will call unlock() to release the mutex.

Sleeping

Everything was working nicely, but the output of the program was fairly zooming off the screen. It’s quite common in multi-threaded code to want to "throttle" the threads in some way. One common means of doing so is to put a thread to sleep for a period. The C++ thread library provides a couple of sleep functions, defined (largely for readability purposes, as far as I can see) in the this_thread namespace. Traditionally, multiple sleep functions (sleep and usleep, for example) have been provided by libraries depending on how long you wanted a thread to sleep, but the C++ threads library makes use of another library feature, the chrono namespace, to define the sleep period in a flexible manner:

void printnums( int start, int n ) {
    for( int i = 0; i < n; i++ ) {
        outnum( start + i );
        this_thread::sleep_for( chrono::milliseconds( 200 ) );
    }
}

This puts the thread to sleep for 0.2 seconds, and results in nicely readable output speed.

Conclusion

This article has looked briefly at the major features (but by no means at all the features) of the C++ Standard Library threads. I was pleasantly surprised at how easy the library was to use, particularly compared with C thread implementations like Windows threads  and pthreads. The use of C++ features makes the much easier to read and to write, and I will definitely be using C++ threads in future projects in preference to native threads, implementations permitting, of course.

Advertisements

From → c++

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: