Re: To thread or not to thread ?
Note to moderator: My previous post was rejected due to excessive
quoting. While I do not agree that i quoted to much, I've removed a few
snippets. If you still believe I quote to much, please advice on which
sections I should trim more.
James Kanze skrev:
JohnQ wrote:
"Le Chaud Lapin" <jaibuduvin@gmail.com> wrote in message
news:1168399666.909953.168890@p59g2000hsd.googlegroups.com...
[snip]
Doesn't ACE attempt to provide a wrapper across current platforms?
That sounds wrought with peril given the discussions here lately.
Probably better to be platform-specific at this juncture.
That's why there's an attempt to standardize. (FWIW: I gave up
on ACE because I found too many errors and false assumptions in
it.)
Actually, as you no doubt know, the "perils of threading" mentioned in
this group are unrelated to whether or not you wrap threading code.
Thus, using ACE or boost::thread will not make your threading code any
more fragile than using the "raw" interface. On the contrary, chances
are that the wrapper will catch some problematic areas and increase
reliability.
I know that after following the thread threads I'm not going
to be turning on the optimization flags on the compiler anytime soon for
one thing.
A good policy even in single threaded code. Until you need the
performance they give.
Depending on the code you produce, you might very well be able to use
non-optimised code as production code. Luckily this has been my case on
many of my older projects with older compilers, that simply could not
produce correct code when optimisations were on.
[snip]
FWIW: the person who developped Boost::threads comes from a
strong Windows background. He adopted a model based on Posix
because he found it significantly superior. (That is, at least,
what I have been told. And from the Boost::threads
documentation: "Event variables are simply far too error-prone.
boost::condition variables are a much safer alternative.") It's
also significant that Vista adopts condition variables; this may
be only because they feel certain that that will be the model
adopted by the C++ standard, and not for any particular
technical reasons. And the Boost library does implement
condition variables under Windows, using Windows OS primitives,
so it must be possible.
I have created multithreading code almost exclusively on Windows, but
never used anything but a small subset of the available API (thread
creation/destruction, critical sections, semaphores and thread local
storage)- and I only once had a use for WaitForMultipleObjects, and
that is so long time ago I no longer remember the reason: Perhaps I did
not have sufficient experience writing MT-code.
Incidentically, my code looked very much like boost threads - with
condition variables being the primary method of "syncronisation".
[snip]
I fell into this trap. The best advice I can give in this
regard is to say that the concept of the thread itself should not be
modeled as a class.
That's exactly how I did it years ago. That's probably where I'd start
from again if/when I dust off and revive that code. I was probably
influenced by the Borland class library and maybe even MFC. So
you think they got it wrong huh?
There are two philosophies: a thread is an asynchronous
function, or a thread is a class. The Boost model tries for a
sort of compromize between the two: a thread is a class, but one
that acts like an implicitly called functional object, and whose
lifetime doesn't need to extend to the lifetime of the entire
thread.
I've never needed to create threads on the fly and thus only have the
"thread as a class" model (a template class represents the class using
a "threadable" base class preventing nasty stuff when you stop the
thread), and I find this model perfectly adequate. This model also
makes it quite easy to stop threads with a minimum of cooperation from
the thread. The only real problem would be in threads doing blocking
IO: here it would be nice with some cooperation from the OS.
In my own work, I distinguish between joinable threads and those
that you "fire and forget". Joinable threads are a class, so
you have something to join with; detached threads are started by
a simple function call.
Another philosophy involves "futures": basically, it consists of
a function to start the thread; the function returns an object
which can later be used for joins.
I have implemented futures, but used a fixed thread-pool for these. I
believe the overhead of creating a new thread means that having generic
worker threads ready for stuff like this is a superior approach.
And of course, it's arguable whether one can even use detached
threads in a correct program. It certainly makes clean shutdown
more difficult. (On the other hand, many of my programs run
forever, so this isn't an issue.)
[snip]
Glancing through the Windows documentation, my impression is
that in addition to condition variables, it is missing some way
of statically initializing mutexes. This would seem essential
to me in order to maintain encapsulation (and well over half of
my pthread_mutex_t are statically initialized). This would be
less of a problem if the compiler allows unsynchronized use of
local static variables with dynamic initialization, but since
one of my target compilers (Sun CC) doesn't, I can't take
advantage of it.
Is that a big problem? I could imagine a (or some) global mutexes to be
used for initialisation of these static variables. Of course, in theory
you might risk a deadlock but in practical life the problem is not big,
and a tiny bit of code might allow you to autoselect what mutex to use
for a
given variable.
Anyway
Ideally, you also want some means of signaling a thread that you
want it to shutdown, along the lines of pthread_cancel. I
haven't seen an equivalent in Windows, and of course, the
semantics of pthread_cancel are not defined in C++ (and
different compilers define them differently), which doesn't help
either. (I might add that there has been enough discussion in
the C++ committee to make me think that there isn't really any
consensus as to what the semantics should be.) In the meantime,
you have to improvise, using some sort of global flag, and
ensuring that no thread does an indefinitly blocking call.
(This typically means that you can't use std::cin.)
As mentioned already, blocking IO is one area where help from the OS
would be appreciated. In real life this has not been a problem (I
assume normal file IO will terminate within a reasonable period of
time, and my socket IO time outs after a reasonable amount of time
anyway). I would not be able to use cin, however.
/Peter
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]