|
Boost : |
From: Jens Maurer (Jens.Maurer_at_[hidden])
Date: 2000-08-22 17:48:29
I would like to remind some obvious things which I think have emanated
from the discussion:
- Concurrent programming is a complex endeavor.
In the traditional world, there is the process abstraction which
basically gives you a virtual single-tasking computer of your own:
virtual memory and virtual CPU (aka timeslices in a pre-emptive
multi-tasking system). This is simple to program, because there is
lots of experience with this model. There are good reasons (e.g.
efficiency) to leave this simple world and have multiple virtual CPUs
on one memory space (threads). This is inherently more complex.
- There is no "one size fits all" or "sliver bullet".
Focusing the view on synchronization, several abstractions are
available for avoiding race conditions and busy waits which are
appropriate at different times: critial sections/mutexes (only one
thread may execute certain portions of code at any given time),
read/write mutexes (several reader threads are allowed in parallel,
but writer threads are exclusive even with respect to readers),
Hoare monitors (critical sections plus notify/wait functionality),
maybe even "raw" Dijkstra semaphores (consumer/producer scenarios).
I think that no amount of library or language wrapping that does not
cripple usefulness can avoid that the programmer has to understand
what's going on, and that means getting acquainted with the complexity.
This is unlike std::map, where the user doesn't have to understand
red-black trees (I don't) to use them correctly (I hope I do
sometimes).
I think that striving for compile-time support for error detection
is a good idea in general (that's why we're using a statically-typed
language), but not very applicable to the problems with threading,
which are run-time dynamic in their most intrinsic nature.
We should, of course, help the programmer, but I think we help him
most by describing safe design patterns, not crippling natural
(common) expressiveness and providing adequate library support for
that.
In particular:
- Don't force the use of lambda expressions if the alternative
is a simple while(...) loop pattern to be explained
- Don't put too much effort in the operator->() auto-lock trick.
That's basically equivalent to the Java "synchronized" keyword
attached to each method, which has likely an inadequate lock level.
- Allow for simple implementations: If the user doesn't need
a recursive mutex, don't force one on him (thus forcing the
underlying system-specific implementation to possibly craft one
together expensively). If the user only needs a critical section,
don't force a monitor on him.
- Implement generic debugging aids, if possible. For example, a
mutex deadlock detection is a must.
- Don't put arbitrary limits on the number of condition variables
attached to one mutex. The buffer example shows that several of
those are useful.
- Hoare's monitor pattern says that the mutex must be locked
around notify() to avoid race conditions. Even though pthreads
doesn't seem to require this, it's still a good idea to follow the
textbook pattern, I think.
- Start thinking about higher-level synchronization abstractions
between threads, for example type-safe (message) queues, and think
about what medium-level options for their implementation should
exist (mutex, monitor, semaphore, etc.).
- Provide a really broad choice of options (recursive vs. non-
recursive mutex, read/write mutex, atomic counters, several
types of monitor), have a trial phase where users implement their
favorite abstract data types / application / whatever with these and
then remove the obviously unused ones.
- Write down the patterns you're trying to introduce. Nowadays,
someone writes "singleton" in a comment at the top of some class
and I can basically skip all the details because I know how it
should work. The goal here is exactly the same: Someone writes
"monitor" on top of some class and the reader should know what's
going on.
C++ can effectively support some design patterns with its advanced
features such as templates (e.g., singleton) so that a detailed
description becomes almost superfluous. I don't have the impression
that concurrent programming patterns are of that nature.
Jens Maurer
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk