Needful Software

Boost.Thread

Introduction

As the name implies the Boost.Thread[1] library provides functions to work with threads.

Thread Creation Example

Our first example will show how to create a new thread. The full example is available from our GitHub repository: BoostTutorials/Thread/Thread1

In this example we use the boost::thread class to create a new thread. The constructor we used creates a new thread and executes the function passed in as argument in that new thread. We also use the boost::thread::join function to wait until the thread we created completes before returning.

The destructor of boost::thread may call std::terminate() if detach() or join() hasn't been called. The exact behavior depends on the compilation settings used. More on this later.

We also use the boost::this_thread::get_id() function to get the thread ID of each thread so we get some info to print on the console.

 File: main.cpp
#include <boost/thread/thread.hpp>
#include <iostream>

void ThreadMain()
{
    std::cout << "Printed from launched thread: " << boost::this_thread::get_id() << std::endl;
}

int main(int argc, char* argv[])
{
    // This creates a thread and immediately starts executing it
    boost::thread thread1(ThreadMain);

    std::cout << "Printed from main thread: " << boost::this_thread::get_id() << std::endl;

    // Wait until thread1 has completed
    thread1.join();

    return 0;
}

Executing the example produces the output below.

 Console output
Printed from main thread: 1a558
Printed from launched thread: 14a80
Press any key to continue . . .

Destructor Behavior

Depending on the Boost compilation settings the behavior of the boost::thread destructor will vary. First we will discuss the behavior that is now recommended and which is also in line with what the std::thread destructor does.

Recommended behavior

The boost::thread destructor will check if the thread is joinable. If it is then it will call std::terminate. The rationale for this behavior is that the developer should make an explicit decision whether the code should wait for the thread to complete (using the join() function) or let it continue after destruction. In the latter case the developer can avoid the call to std::terminate by calling detach() to detach the actual thread from the boost::thread object.

The reason for forcing the developer to make an explicit choice is that either implicit choice would have bad consequences in some programs:

  • If the destructor called join() delays would be introduced in the program silently.
  • If the destructor called detach() the bugs that could result would be very hard to investigate.

Deprecated behavior

In previous versions of Boost the destructor would actually go for the second option described above and call detach(). There isn't any good reason to rely on this old behavior as if it is the desired behavior it can simply be obtained by explicitly calling detach() before the destructor is called. That way the code will work regardless of the Boost compilation settings.

Thread Function Exception Example

Uncaught exceptions in threads created with boost::thread will cause std::terminate to be called. The exception to that rule is the boost::thread_interrupted exception which we will be discussing later.

In the example below we illustrate the uncaught exception behavior by providing a custom std::terminate handler that prints a message on the console. We then throw an exception from the child thread. The full example is available from our GitHub repository: BoostTutorials/Thread/ThreadException1

 File: main.cpp
#include <boost/thread/thread.hpp>
#include <iostream>

void Terminate()
{
    std::cout << "terminate handler called" << std::endl;
    abort();
}

void ThreadMain()
{
    // On some platforms (including Visual Studio) set_terminate
    // only applies to the current thread so we call it here
    // rather than in the main thread.
    std::set_terminate(Terminate);
    throw std::runtime_error("ThreadMain exception");
}

int main(int argc, char* argv[])
{
    // This creates a thread and immediately starts executing it
    boost::thread thread1(ThreadMain);

    // Wait until thread1 has completed but abort() will actually be
    // called so this will never return
    thread1.join();

    return 0;
}

This produces the following output.

 Console output
terminate handler called
Press any key to continue . . .

References

  1. Boost.Thread: official documentation (boost.org)