0

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:

  1. Get the first thread in the vector
  2. Get the Gizmo object contained within that thread
  3. Set it's 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().

John Dibling
  • 99,718
  • 31
  • 186
  • 324

3 Answers3

2

Your start_thread() method is creating a temporary, stack object that is then being passed to the thread. Instead, you should create a vector of Gizmo objects in your main(), and then pass each member of that vector(a Gizmo) to boost's thread constructor. I don't see any reason why you should specifically obtain the thread's callable object when you can store a reference to it in a vector in your main function.

Remember to synchronize setting of the state between the main thread and the boost thread that checks it.

Specksynder
  • 813
  • 9
  • 20
  • +1 I see what you're getting at, similar to another suggestion here. The reason I didn't want to maintain a list of `Gizmo`s and another list of `thread`s is because the thread already has the `Gizmo`, encapsulated within it's data members. Maintaining two seperate lists, and keeping them synchronized, is a level of complexity that should not be necesarry. At least, in an ideal world -- which boost, apparently, is not. – John Dibling Jun 09 '12 at 17:23
1

Could you not implement a start_thread class, giving you access to the functor, instead of a function, and keep a vector of pointers to these? Something along the lines of (details to be ironed out, none of this is tested):

class start_thread {
 public:
  explicit start_thread(char c) : gizmo_(std::string(c,1)), t_(gizmo_) {}
  void join() { t_.join();}
  const Gizmo& callable() const { return gizmo_;}
  Gizmo& callable() { return gizmo_;}
 private:
  Gizmo gizmo_;
  boost::thread t_;
}

You need to pass the gizmo_ to the thread constructor as a boost::ref(gizmo_).

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • +1: Yes, I suppose that I could. I had hoped that I could use built-in facilities, rather than constructing some kind of encapsulating device. By doing as you suggest, I'll also have to implement some kind of container class with a `join_all()` type function, etc. I can do all this; I'd just rather not if I don't have to. – John Dibling Jun 09 '12 at 16:47
  • @JohnDibling yes, it is a pity there isn't a handle to the callable, but I guess that could complicate synchronization. – juanchopanza Jun 09 '12 at 17:02
1

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.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169