threads (was Re: [gclist] Re: gclist-digest V3 #84)
Mon, 26 Jun 2000 11:36:01 -0700
> -----Original Message-----
> From: Scott Meyer [mailto:firstname.lastname@example.org]
> Sent: Sunday, June 25, 2000 10:43 PM
> To: Bob Kerns; 'Andrew Chen'; email@example.com
> Subject: RE: threads (was Re: [gclist] Re: gclist-digest V3 #84)
> The Point to which I was perhaps too obliquely alluding is that
> programmers have (repeatedly) "invented" process-like things and,
> in point of fact, use them to accomplish a very large proportion
> of the "real" (ie. non-programmers actually pay money to have it
> occur) computation that takes place in the world. This pattern
> suggests that there is some value to this notion of process that
> makes the "extra weight" worth carrying. Comparing a thread
> to a process and claiming the former to be "lightweight" is
> akin to comparing a chassis to a complete car. Of course the
> chassis is lighter... You need to compare complete applications.
> For example, if you have to handle input on 100 sockets is it
> more heavyweight to fork 100 threads and let them block in read,
> or to have one process blocked in poll? Like most things IRL,
> not quite so clear-cut.
> Speaking of actual implementations of threads and processes,
> there is some difference (page tables, acls, etc.) between the
> two, such that threads appear to be somewhat cheaper than
> processes. Unfortunately, (for thread-partisans) the nominally
> cheaper threaded environment comes at the cost of a heavy
> burden of heretofor "system-level" mutual-exclusion issues.
> The naive approach to resolving these issues, making all libraries
> thread-safe, has a disasterous synchronization overhead and
> is very difficult to debug. Most recently, this issue seems to have
> motivated the Java architects to rediscover the idea (previously
> rediscovered by the Smalltalk crowd) of leaving most libraries
> unsynchronized and providing a small set of synchronized data
> structures. Or at least trying to.
> In my experience, most successful uses of threads come very
> close to that model or to the purely functional varient
> suggested earlier in this discussion. In practice it is
> pretty hard to measure much difference between this stylized
> use of threads and processes with shared memory. Pragmatically,
> it is a hell of a lot easier to implement the process version
> of things.
> Scott Meyer 415 506 0987
> Principal Member of Technical Staff
> Oracle - Languages & Relational Technology standard disclaimer
It seems to me that we're mixing together a whole bunch of issues here.
I'll assert that:
1) Implicit fine-grain locking in library code is a mistake, whether you use
processes or threads. Nobody does this for processes. It doesn't make much
sense for threads, except possibly when it's needed to ensure safety of the
code. The level at which locking is needed is application dependent, and
usually larger than a single library operation. Java got this somewhat
wrong the first time around, but more recent parts of the library try to
correct it. (I think we agree here.)
2) Whether or not you use poll/select is largely orthogonal to the threads
issue. Applications can be multithreaded (e.g. because they need to run on
MPs) and still use poll/select (e.g. because it's more efficient in a
particular environment). I believe Java doesn't support poll/select at all,
but that's a Java-specific issue.
3) I've run into a fair amount of multithreaded code that accesses shared
state. I'm working on a garbage collector that works this way. My
impression is that most high performance numerical code is written this way,
unless it needs to run on message-passing machines. Some other code is
written this way mostly for structuring reasons. I think this applies to
Netscape and Mozilla, for example.
4) It's usually a mistake to use processes when you need to share lots of
state, though databases may well be an exception to that rule. Aside from
performance issues, using processes means that there's no guarantee that
that pointers point to the same thing in all participating processes. This
is not an intuitive model. I've heard complaints from many people who've
tried to put C++ objects with virtual functions into shared heaps using this
scheme, and are then surprised when it breaks. It can't work (unless you
basically reimplement threads). There's no guarantee that my vtable is at
the same address as your vtable if you're in a different process.
5) Until fairly recently, you couldn't use threads in portable code, and
expect them to take advantage of multiprocessors correctly. You could do so
by building on processes. I think this has finally changed, but it probably
explains a lot of the processes-with-shared-memory implementations.
Standard disclaimer ...