Thread of Execution

In this article by Anton Polukhin Alekseevic, the author of the book Boost C++ Application Development Cookbook - Second Edition, we will see the multithreading concept. 

Multithreading means multiple threads of execution exist within a single process. Threads may share process resources and have their own resources. Those threads of execution may run independently on different CPUs, leading to faster and more responsible programs.

Let's see how to create a thread of execution.

(For more resources related to this topic, see here.)

Creating a thread of execution

On modern multicore compilers, to achieve maximal performance (or just to provide a good user experience), programs usually must use multiple threads of execution. Here is a motivating example in which we need to create and fill a big file in a thread that draws the user interface:

#include <algorithm> 
#include <fstream> 
#include <iterator> 


bool is_first_run(); 
 
// Function that executes for a long time.
void fill_file(char fill_char, std::size_t size, const char* filename);
 
// ... 


// Somewhere in thread that draws a user interface:
if (is_first_run()) { 
    // This will be executing for a long time during which 
    // users interface freezes. 
    fill_file(0, 8 * 1024 * 1024, "save_file.txt"); 
} 

Getting ready

This recipe requires knowledge of boost::bind or std::bind.

How to do it...

Starting a thread of execution never was so easy:

#include <boost/thread.hpp> 
 
// ... 
// Somewhere in thread that draws a user interface:
if (is_first_run()) { 
    boost::thread(boost::bind( 
        &fill_file, 
        0, 
        8 * 1024 * 1024, 
        "save_file.txt" 
    )).detach(); 
} 

How it works...

The boost::thread variable accepts a functional object that can be called without parameters (we provided one using boost::bind) and creates a separate thread of execution. That functional object is copied into a constructed thread of execution and run there.

We are using version 4 of the Boost.Thread in all recipes (defined BOOST_THREAD_VERSION to 4). Important differences between Boost.Thread versions are highlighted.

After that, we call the detach() function, which does the following:

  • The thread of execution is detached from the boost::thread variable but continues its execution
  • The boost::thread variable start to hold a Not-A-Thread state

Note that without a call to detach(), the destructor of boost::thread will notice that it still holds a OS thread and will call std::terminatestd::terminate terminates our program without calling destructors, freeing up resources, and without other cleanup.

Default constructed threads also have a Not-A-Thread state, and they do not create a separate thread of execution.

There's more...

What if we want to make sure that file was created and written before doing some other job? In that case we need to join the thread in the following way:

// ... 
// Somewhere in thread that draws a user interface:
if (is_first_run()) { 
    boost::thread t(boost::bind( 
        &fill_file, 
        0, 
        8 * 1024 * 1024, 
        "save_file.txt" 
    )); 
    // Do some work.
    // ...


    // Waiting for thread to finish.
   t.join(); 
} 

After the thread is joined, the boost::thread variable holds a Not-A-Thread state and its destructor does not call std::terminate.

Remember that the thread must be joined or detached before its destructor is called. Otherwise, your program will terminate!

With BOOST_THREAD_VERSION=2 defined, the destructor of boost::thread calls detach(), which does not lead to std::terminate. But doing so breaks compatibility with std::thread, and some day, when your project is moving to the C++ standard library threads or when BOOST_THREAD_VERSION=2 won't be supported; this will give you a lot of surprises. Version 4 of Boost.Thread is more explicit and strong, which is usually preferable in C++ language.

Beware that std::terminate() is called when any exception that is not of type boost::thread_interrupted leaves boundary of the functional object that was passed to the boost::thread constructor.

There is a very helpful wrapper that works as a RAII wrapper around the thread and allows you to emulate the BOOST_THREAD_VERSION=2 behavior; it is called boost::scoped_thread<T>, where T can be one of the following classes:

  • boost::interrupt_and_join_if_joinable: To interrupt and join thread at destruction
  • boost::join_if_joinable: To join a thread at destruction
  • boost::detach: To detach a thread at destruction

Here is a small example:

#include <boost/thread/scoped_thread.hpp> 


void some_func(); 


void example_with_raii() { 
    boost::scoped_thread<boost::join_if_joinable> t( 
        boost::thread(&some_func) 
    ); 


    // 't' will be joined at scope exit.
} 

The boost::thread class was accepted as a part of the C++11 standard and you can find it in the <thread> header in the std:: namespace. There is no big difference between the Boost's version 4 and C++11 standard library versions of the thread class. However, boost::thread is available on the C++03 compilers, so its usage is more versatile.

There is a very good reason for calling std::terminate instead of joining by default! C and C++ languages are often used in life critical software. Such software is controlled by other software, called watchdog. Those watchdogs can easily detect that application has terminated but not always can detect deadlocks or detect them with bigger delays. For example for a defibrillator software it's safer to terminate, than hang on join() for a few seconds waiting for a watchdog reaction. Keep that in mind while designing such applications.

See also

  • All the recipes in this chapter are using Boost.Thread. You may continue reading to get more information about the library.
  • The official documentation has a full list of the boost::thread methods and remarks about their availability in the C++11 standard library. The official documentation can be found at http://boost.org/libs/thread.
  • The Interrupting a thread recipe will give you an idea of what the boost::interrupt_and_join_if_joinable class does.

Summary

We saw how to create a thread of execution using some easy techniques.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Boost C++ Application Development Cookbook - Second Edition

Explore Title