Re: Basic Question on POSIX Threads
On Oct 15, 1:01 am, Gianni Mariani <gi4nos...@marian.ws> wrote:
Laurent D.A.M. MENTEN wrote:
Here is a skeleton of how I handle pthreads (the same pattern works with
win32 threads) in C++; Ther is of course a lot more to write for the
thread management itself but the importants things are there. I use it
by writting a derived class that overload the run() method.
Hope it helps.
class Thread
{
static void* glue( void* );
private:
pthread_t tid;
pthread_attr_t attr;
public:
Thread();
virtual void* run() = 0;
};
void* Thread::glue( void* t )
{
return ((Thread*)t)->run();
}
Thread::Thread()
{
// ...
pthread_create( &this->tid, &this->attr, Thread::glue, (void*)this );
// ...
}
That code has a major race condition issue.
You're right about the race condition, of course, and I doubt
that his code would work on a modern system (dual core or more).
But it won't even compile with a conformant C++ compiler. His
function glue must be `extern "C"', and thus cannot be a member.
(On the other hand, it *can* be static, in order to avoid all
risk of name collision.)
Never invoke the thread on
a virtual function until you can guarantee the the object is fully
constructed. Some argue that it's sufficient to "unleash" the thread at
the most derived constructor and wait for it's termination at the most
derived destructor, some argue that you can't unleash the thread until
the constructor has returned and that you can't even call delete until
the thread has been terminated.
I'm not sure what the difference is, but globally, if you're
trying to deal with Thread objects:
-- The obvious solution is to use the strategy pattern, rather
than the template method solution. Move the virtual
function run() over into a separate ThreadExec interface,
and pass a ThreadExec* to the constructor of Thread.
Depending on your goals, you could specify transfer of
ownership (with the destructor of Thread deleting it) or
not.
-- Personally, I don't like the idea of a starting a thread in
the constructor anyway. But it's really just a vague uneasy
feeling, which I can't really justify if the strategy
pattern is used. If the constructor doesn't start the
thread, then there's no problem with using the template
method pattern, however.
-- With regards to lifetime: obviously, any "object" must
continue to live as long as any code references members in
it. (This can be a serious consideration if you are
starting a detached thread.)
The code below shows how to create a thread object (in this
case called Task). This is similar to the Austria C++ code
however the Austria C++ code is portable across Win32 and
Pthreads. Use Austria or boost threads. You'll see that it's
similar to your code but the virtual method is not called
until after the the "start" method is called.
At the cost of an additional lock/conditional variable. This is
the solution adopted in Boost threads as well, for different
reasons. Boost copies the functional object, and needs the
conditional to ensure that the copy is finished. In this case,
the additional lock is an acceptable cost in order to allow
transparent use of local, or even temporary, functional objects.
If you're not doing this, i.e. you require derivation from a
Thread (or ThreadExec) class, and you require the objects
lifetime to extend to the end of the thread, there's no point in
it.
My own experience is that detached threads and non detached
threads are really two different things, and require different
solutions. For detached threads, I don't use a Thread object at
all---just a function to start the thread, which works more or
less like the Boost threads, copying the functional object into
the new thread, and synchronizing to avoid returning from the
function until the copy has finished. For non-detached threads,
on the other hand, I use something like what the original poster
proposed, but with the strategy pattern rather than the template
method pattern---the return value from joining is a pointer to
the ThreadExec object, so you can easily recover any values the
thread may have calculated, and delete it, if it was originally
dynamically allocated.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34