Skip to main content

C++11 std::thread Construction and Assignment Tricks





C++11 arrived with std::thread and related classes built within the standard. With the move semantics in place, the construction and assignment practices of 'std threads' deserve a small write up.

Let's define a 'Runnable' first:

class Runnable
{
   public:
      virtual void operator ()() = 0;
      virtual ~Runnable(){}
};


class Task : Runnable
{
     public:
         void operator ()()
         {
              //do sth here..
         }
};
                                         

      //later..

     #include <thread>

    Task runnable;

    std::thread t1; //not attached to any thread  
                                         
    std::thread t2 ( runnable ); //attached and running

 

Copy Construction of std::thread is not allowed


           std::thread t3 ( t2 ); // compilation error

           std::thread t4 = t2; // compilation error

error : use of deleted function std::thread (const std::thread&); 

This is because the copy constructor has been marked as deleted in C++11.

Assignment is also not allowed


           t1 = t2; // compilation error

error : use of deleted function std::thread& operator=(const std::thread&);

This is because the assignment operator has been marked as deleted in C++11.


Construction with move semantics is the only way

      

           std::thread t5 ( std::move ( t2 ) );  // ok
                                         
                           or
                                     
           std::thread t6 = std::move ( t2 ) ; // ok

                          or

           std::thread t6 ( std::thread (task) ) ; // ok - temporaries don't require explicit 'move'

                         or

          std::thread t6  = std::thread (task) ; // ok - temporaries don't require explicit 'move'

All this is possible because std::thread class has folllowing constructors defined:


thread()

     // not attached
thread( thread&& other );
     // with move semantics - takes ownership from passed ref



                                         

Assignment with move semantics is also allowed


                          std::thread t7;

                          t7 = std::move ( t2 ); // ok

                             or

        t7 = std::thread ( task ); // ok - temporaries don't require explicit 'move'

After all these invocations, only 't7' is referring to underlying thread and 't2' has relinquished ownership of underlying.

What if 't7' already had a thread attached ?


                          std::thread t7 ( anotherTask ) ;    

                          t7 = std::move ( t2 );

The real thread attached to 't7' will get terminated (if still alive) because there is no 'visible' handle to it anymore. That will cause unexpected effects and the program may crash.

If real thread was alive, C++ run-time will invoke std::terminate which calls the currently installed std::terminate_handler. The default std::terminate_handler calls std::abort.  Which creates a core dump with program termination.

To avoid this situation, properly 'detach' the thread or 'wait' for it to die.

                          std::thread t7 ( sometask ) ;
                          t7.detach();

This will leave the real thread running as a daemon and now 't7' can become a handle to another real thread as follows:

                          t7 = std::move ( t2 );


 References:

http://en.cppreference.com/w/cpp/thread/thread/thread    

The images on this page are of Emperor SamudraGupta, one of the greatest Indian kings, warrior and a patron of art circa 335 - 375 CE.

More details here: http://en.wikipedia.org/wiki/Samudragupta

Comments

Popular posts from this blog

C++ logging using Apache Log4cxx on Linux

I'd demonstrate the set up of Apache log4cxx on Ubuntu 12.x in following steps: 1. Download all required packages 2. Build Log4cxx via Apache Maven 3. Use the libraries in a NetBeans C++ project on Ubuntu Linux. Reference URL: http://www.yolinux.com/TUTORIALS/Log4cxx.html This URL is quite detailed and explains things for other *nix operating systems as well. I wanted to start with minimum steps required, hence this post. I have a Windows 7 desktop and have Ubuntu 12.x 64-bit running on it via Oracle Virtualbox. So Ubuntu is running as a guest on my Windows 7 host. [The reference URL mentions different versions of  'libaprXX' libs but we have to use 'libapr1-dev' and 'libaprutil-dev', will see that later.] Prerequisites (install all of the following one by one) autoconf and automake --> sudo apt-get install autoconf automake libxml2 and libxml2-devel ( http://xmlsoft.org/ ) --> sudo apt-get install libxml2 libxml2-dev gmp (

..Where Apache Camel meets Spring JMS

This post is relevant for developers who are aware of Apache Camel and use its JMS component to consume from a JMS broker. We are only discussing the message consumption part here. I'm assuming you are conversant with Camel's terminology of routes, endpoints, processors and EIPs. If not, following is a good place to start: Link --->  Apache Camel Home   Ever wondered what happens underneath when a Camel consumer route is defined to take messages from a JMS endpoint ? This post is an attempt to explain how Camel integrates with JMS via Spring APIs and how the business logic defined in a Camel route is invoked ? Following is a simplified sequence diagram showing the set-up process which will eventually lead to message consumption from JMS broker. . Creation of JMS endpoints in a camel context is initiated by definition (via Java/XML DSL). A JMS endpoint takes a number of configurable attributes as seen here Camel JMS Component . As soon as context is bootstrappe