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..
}
};
#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/threadThe 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
Post a Comment