I am using Boost 1.49 and MSVC10.
If a boost::thread
is constructed with a callable object1, and that object has member functions or variables I want to access from outside the context of the thread
, how do I get to the callabe object?
For example, I have implemmented a simple application that spawns 5 worker threads, saved to a vector<boost::thread*>
local to main()
. Each of those threads is instantiated with a callable object, Gizmo
which takes one char
parameter in its constructor. This char
is saved as a std::string
member variable in the Gizmo
class. Each thread will cout
the saved string
, then sleep for 250 ms. It continues in this loop forever, until somehow the saves string
's value becomes "die".
Short, Self Contained, (Hopefully) Correct, Example:
#include <cstdlib>
#include <string>
#include <memory>
#include <vector>
using namespace std;
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
boost::mutex cout_mtx;
class Gizmo
{
public:
Gizmo(const string& state) : state_(state) {};
Gizmo(Gizmo&& rhs) : state_(std::move(rhs.state_)) {};
virtual ~Gizmo()
{
bool b = true;
}
void operator()();
string state_;
};
void Gizmo::operator()()
{
while( state_ != "die" )
{
{
boost::mutex::scoped_lock scoped_lock(cout_mtx);
cout << state_ << flush;
}
boost::this_thread::sleep(boost::posix_time::milliseconds(250));
}
}
boost::thread* start_thread(char c)
{
Gizmo g(string(1,c));
return new boost::thread(g);
}
int main()
{
vector<boost::thread*> threads;
string d=".*x%$";
for( string::const_iterator it = d.begin(); it != d.end(); ++it )
{
threads.push_back(start_thread(*it));
}
for( auto th = threads.begin(); th != threads.end(); ++th )
(*th)->join();
}
Now I want to make a code change in main()
which will:
thread
in the vector
Gizmo
object contained within that thread
state_
to "die"How do I get the Gizmo
within the thread?
for( auto th = threads.begin(); th != threads.end(); ++th )
{
boost::this_thread::sleep(boost::posix_time::seconds(1));
Gizmo& that_gizmo = (*th)-> ??? ; // WHAT DO I DO?
that_gizmo.state_ = "die";
}
(1) A callable object in this context means a class
with an operator()
.
The boost::thread
does not provide the ability to get the callable object. boost::thread
has a default constructor, so it does not know the callable type or function that will serve as the thread's entry point. Also, if boost::thread
did not perform type erasure, then it would no longer be possible to manage threads with different entry points in the same collection or thread pool. For example, std::vector< boost::thread< Gizmo > >
could only manage Gizmo
threads, while std::vector< boost::thread >
can manage Gizmo
threads and non-Gizmo
threads.
Instead of having to manage Gizmo
and boost::thread
objects in separate list, or creating a new type to pair the two together, consider associating the two via std::pair
or boost::tuple
. For example:
std::vector< boost::tuple< boost::thread*, Gizmo > > threads;
Also, if the threads end up having multiple types used for the entry point, and each type requires its own form of shutdown, consider performing type erasure via boost::function
.
For example:
void shutdown_gizmo( Gizmo& );
boost::function< void() > fn = boost::bind( shutdown_gizmo, boost::ref( gizmo ) );
// The tuple now contains a function object, decoupling it from Gizmo objects,
// and allowing other shutdown methods to be paired with threads.
typedef boost::tuple< boost::thread*, boost::function< void() > > tuple_t;
std::vector< tuple_t > threads;
With any solution, just be careful about maintaining the scope of the Gizmo
object, as well as maintaining a handle to the correct Gizmo
object. It can be fairly easy to unintentionally obtain handles to copies of the actual Gizmo
object.