Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Accessing a thread's object

0 views
Skip to first unread message

Ryan

unread,
Aug 8, 2006, 6:04:47 PM8/8/06
to
{ Boost has its own mailing lists and forums, see www.boost.org for
the ways to access those. Please take time to read the newsgroup
policies, the link is below, in the moderation server banner. -mod }

I'm using boost::threads and trying to access the members of the
functor that I start the thread on and can't find information on how to
do it anywhere.

It took me a while to even figure out what was going on but eventually
I determined that creating a new thread actually just COPIES your
functor object. Therefore changing the object's member values from
within the thread has NO effect whatsoever on the object you used to
initiate the thread.

Basically what I'm doing is I create object A. Object A has the ()
operator that boost threads automatically start runnning.

I tell boost I want to start A on it's own thread.

A does some stuff (in this case 'A' is a thread dedicated to serving a
client and my program creates a new A for every client that connects so
that it can cater to everyone at once).

The problem is, if I then call A.getWhatever() it returns null because
everything that has been changed, to my absolute delight, has been
changed on the boost::thread's copy of A and completely ignored the one
I passed into the thread() function.

Basically all I want to know is if there is any way to just access the
thread's local copy of the object. If not it looks like my only other
option is to pass in a pointer to another object that will hold all of
the state data which will mean rewriting a HUGE amount of code :(

Let me know if I've left anything out. I can barely comprehend most of
the stuff I'm doing in this program myself lol. Also this is my first
time using one of these boards so, sorry if I'm not following
convention or whatnot.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Peter Dimov

unread,
Aug 8, 2006, 10:00:50 PM8/8/06
to
Ryan wrote:

> I'm using boost::threads and trying to access the members of the
> functor that I start the thread on and can't find information on how to
> do it anywhere.
>
> It took me a while to even figure out what was going on but eventually
> I determined that creating a new thread actually just COPIES your
> functor object. Therefore changing the object's member values from
> within the thread has NO effect whatsoever on the object you used to
> initiate the thread.
>
> Basically what I'm doing is I create object A. Object A has the ()
> operator that boost threads automatically start runnning.
>
> I tell boost I want to start A on it's own thread.
>
> A does some stuff (in this case 'A' is a thread dedicated to serving a
> client and my program creates a new A for every client that connects so
> that it can cater to everyone at once).
>
> The problem is, if I then call A.getWhatever() it returns null because
> everything that has been changed, to my absolute delight, has been
> changed on the boost::thread's copy of A and completely ignored the one
> I passed into the thread() function.

boost::thread makes a copy by default since it likes to manage the
lifetime of the function object and prevent its destruction before the
thread has ended. If you do

void f()
{
MyThreadObject mt;
boost::thread th( mt );
}

mt will be destroyed at the end of f() but unless a copy is made, the
thread will continue to access it, potentially overwriting the stack of
the caller.

If you really don't want a copy, use boost::ref:

boost::thread th( boost::ref( mt ) );

but now you'd have to make sure that mt stays alive until the thread is
done executing it.

You can use shared_ptr to automate that:

boost::shared_ptr<MyThreadObject> pmt( new MyThreadObject );
boost::thread th( boost::bind( &MyThreadObject::operator(), pmt ) );

Now the thread will manage its own copy of pmt, but you can still use
pmt->getWhatever (assuming appropriate locking.)

If you go with the last option, you can rename operator() to something
like 'run', if you like.

James Kanze

unread,
Aug 15, 2006, 2:12:41 PM8/15/06
to
Ryan wrote:
> { Boost has its own mailing lists and forums, see www.boost.org for
> the ways to access those. Please take time to read the newsgroup
> policies, the link is below, in the moderation server banner. -mod }

Given that Boost is multi-platform, and very much a part of C++,
I would have thought that some discussions concerning its use
would be appropriate here. In the case of threading,
particularly: I'm pretty sure that the next version of the
standard will address threading in some way or another, and
there is a definite possibility that Boost threads will be part
of that.

> I'm using boost::threads and trying to access the members of
> the functor that I start the thread on and can't find
> information on how to do it anywhere.

> It took me a while to even figure out what was going on but
> eventually I determined that creating a new thread actually
> just COPIES your functor object. Therefore changing the
> object's member values from within the thread has NO effect
> whatsoever on the object you used to initiate the thread.

This is actually documented, although it surprised me at first
as well, as it is different from every other threading library
I've seen.

Having played around with it some, I"ve come to conclude that
this is a feature, and not a defect. To begin with, it
certainly fits into the basic C++ model, in which everything is
a value (and gets copied), unless you specifically say
otherwise. In addition, boost::thread can start a detached
thread, which can continue running beyond the lifetime of the
thread object---and the functional object you passed it. (I'm
not sure that this can be considered a feature. Detached and
joinable threads are two different beasts, and having a thread
that suddenly becomes detached because you forgot to catch a
signal is not what I consider a good idea. On the other hand,
what do you do if the destructor is called before the thread is
finished?)

I'm still experimenting with the best way to handle joinable
threads, with return values, and possibly exceptions that you
want to propagate accross the thread boundary. The key,
however, seems to turn on std::auto_ptr in the functional
object: this means that everything must be dynamically
allocated, but it also means that the lifetime is managed, and
an object used by the thread is neither accessible nor destroyed
as long as the thread is running. For return values, it also
means that the invoker cannot access the value before the join
has occurred. (Note that I've not had the time yet to do any
concrete coding; I've been too involved with other things for
the moment. So this is very much a thought experiment, which
might not work out in practice---auto_ptr can be a tricky beast,
even if its semantics correspond to exactly what you want here.)

If you're allocating the thread object on the stack, I'd also
suggest wrapping it in some sort of user defined class which
will abort if the destructor is called before the join. In
specific systems you might be able to do better, and cancel the
thread in some way (which can also be tricky). Two things you
definitly don't want to do however: 1) detach the thread, and
let it continue running, and 2) wait for its normal termination
in the destructor.

> Basically what I'm doing is I create object A. Object A has
> the () operator that boost threads automatically start
> runnning.

> I tell boost I want to start A on it's own thread.

> A does some stuff (in this case 'A' is a thread dedicated to
> serving a client and my program creates a new A for every
> client that connects so that it can cater to everyone at
> once).

> The problem is, if I then call A.getWhatever() it returns null
> because everything that has been changed, to my absolute
> delight, has been changed on the boost::thread's copy of A and
> completely ignored the one I passed into the thread()
> function.

Somewhere I read that the solution to every programming problem
is an additonal level of indirection. It certainly is the
answer here. For a joinable thread, with a result, the
functional object which you pass to boost::thread should be a
simple forwarder to the actual object. Note, however, that
lifetime management and access control for this object can be
decidedly non-trivial. Consider what happens, for example, in
the following cases:

-- the thread is still running, and the invoking code gets an
exception, and

-- the thread has actually terminated, and the invoking code
gets an exception before having done the join (and regained
possesion of the object).

In the first case, you cannot destruct the object, since the
thread is still using it; on the other hand, the thread has to
destruct it in some way, since the invoking thread is no longer
there to do it. In the second case, the invoking thread has to
destroy the object, although it doesn't yet know about it.

> Basically all I want to know is if there is any way to just
> access the thread's local copy of the object. If not it looks
> like my only other option is to pass in a pointer to another
> object that will hold all of the state data which will mean
> rewriting a HUGE amount of code :(

I don't see how it could be such a HUGE amoung of code; all you
need to do is provide wrappers, and use them when invoking the
thread or joining with it. And you shouldn't be invoking or
joining in that many places.

And I suspect that the original code had a few errors anyway, in
cases where the boost::thread object was destroyed by stack
clean-up after an exception.

The problem is not simple. Boost only offers some very, very
low level wrappers for the basic thread primitives, without
pretending to solve it. Probably because there is no
universally recognized solution; each problem requires
considerable customization.

> Let me know if I've left anything out. I can barely
> comprehend most of the stuff I'm doing in this program myself

In which case, the code won't work. Threading is NOT trivial,
and very much requires that you know what you are doing. Unless
you carefully control who accesses what when, your code will
contain subtle errors, which generally won't show up during
testing.

--
James Kanze kanze...@neuf.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

0 new messages