Fake threads (was [Python-Dev] ActiveState & fork & Perl)

Tim Peters tim_one@email.msn.com
Thu, 1 Jul 1999 00:30:30 -0400


[Guido]
> I guess it's all in the perspective.  99.99% of all thread apps I've
> ever written use threads primarily to overlap I/O -- if there wasn't
> I/O to overlap I wouldn't use a thread.  I think I share this
> perspective with most of the thread community (after all, threads
> originate in the OS world where they were invented as a replacement
> for I/O completion routines).

Different perspective indeed!  Where I've been, you never used something as
delicate as a thread to overlap I/O, you instead used the kernel-supported
asynch Fortran I/O extensions <0.7 wink>.

Those days are long gone, and I've adjusted to that.  Time for you to leave
the past too <wink>:  by sheer numbers, most of the "thread community"
*today* is to be found typing at a Windows box, where cheap & reliable
threads are a core part of the programming culture.  They have better ways
to overlap I/O there too.  Throwing explicit threads at this is like writing
a recursive Fibonacci number generator in Scheme, but building the recursion
yourself by hand out of explicit continuations <wink>.

> ...
> As far as I can tell, all the examples you give are easily done using
> coroutines.  Can we call whatever you're asking for coroutines instead
> of fake threads?

I have multiple agendas, of course.  What I personally want for my own work
is no more than Icon's generators, formally "semi coroutines", and easily
implemented in the interpreter (although not the language) as it exists
today.

Coroutines, fake threads and continuations are much stronger than
generators, and I expect you can fake any of the first three given either of
the others.  Generators fall out of any of them too (*you* implemented
generators once using Python threads, and I implemented general
coroutines -- "fake threads" are good enough for either of those).

So, yes, for that agenda any means of suspending/resuming control flow can
be made to work.  I seized on fake threads because Python already has a
notion of threads.

A second agenda is that Python could be a lovely language for *learning*
thread programming; the threading module helps, but fake threads could
likely help more by e.g. detecting deadlocks (and pointing them out) instead
of leaving a thread newbie staring at a hung system without a clue.

A third agenda is related to Mark & Greg's, making Python's threads "real
threads" under Windows.  The fake thread agenda doesn't tie into that,
except to confuse things even more if you take either agenda seriously <0.5
frown>.

> I think that when you mention threads, green or otherwise colored,
> most people who are at all familiar with the concept will assume they
> provide I/O overlapping, except perhaps when they grew up in the
> parallel machine world.

They didn't suggest I/O to me at all, but I grew up in the disqualified
world <wink>; doubt they would to a Windows programmer either (e.g., my
employer ships heavily threaded Windows apps of various kinds, and
overlapped I/O isn't a factor in any of them; it's mostly a matter of
algorithm factoring to keep the real-time incestuous subsystems from growing
impossibly complex, and in some of the very expensive apps also a need to
exploit multiple processors).  BTW, I called them "fake" threads to get away
from whatever historical baggage comes attached to "green".

> Certainly all examples I give in my never-completed thread tutorial
> (still available at
> http://www.python.org/doc/essays/threads.html) use I/O as the primary
> motivator --

The preceding "99.99% of all thread apps I've ever written use threads
primarily to overlap I/O" may explain this <wink>.  BTW, there is only one
example there, which rather dilutes the strength of the rhetorical "all" ...

> this kind of example appeals to simples souls (e.g. downloading more than
> one file in parallel, which they probably have already seen in action in
> their web browser), as opposed to generators or pipelines or coroutines
> (for which you need to have some programming theory background to
> appreciate the powerful abstraction possibillities they give).

I don't at all object to using I/O as a motivator, but the latter point is
off base.  There is *nothing* in Comp Sci harder to master than thread
programming!  It's the pinnacle of perplexity, the depth of despair, the
king of confusion (stop before I exaggerate <wink>).

Generators in particular get re-invented often as a much simpler approach to
suspending a subroutine's control flow; indeed, Icon's primary audience is
still among the humanities, and even dumb linguists <wink> don't seem to
have notable problems picking it up.  Threads have all the complexities of
the other guys, plus races, deadlocks, starvation, load imbalance,
non-determinism and non-reproducibility.

Threads simply aren't simple-soul material, no matter how pedestrian a
motivating *example* may be.  I suspect that's why your tutorial remains
unfinished:  you had no trouble describing the problem to be solved, but got
bogged down in mushrooming complications describing how to use threads to
solve it.  Even so, the simple example at the end is already flawed ("print"
isn't atomic in Python, so the

    print len(text), url

may print the len(text) from one thread followed by the url from another).

It's not hard to find simple-soul examples for generators either (coroutines
& continuations *are* hard to motivate!), especially since Python's
for/__getitem__ protocol is already a weak form of generator, and xrange
*is* a full-blown generator; e.g., a common question on c.l.py is how to
iterate over a sequence backwards:

    for x in backwards(sequence):
        print x

    def backwards(s):
        for i in xrange(len(s)-1, -1, -1):
            suspend s[i]

Nobody needs a comp sci background to understand what that *does*, or why
it's handy.  Try iterating over a tree structure instead & then the *power*
becomes apparent; this isn't comp-sci-ish either, unless we adopt a "if
they've heard of trees, they're impractical dreamers" stance <wink>.  BTW,
iterating over a tree is what os.path.walk does, and a frequent source of
newbie confusion (they understand directory trees, they don't grasp the
callback-based interface; generating (dirname, names) pairs instead would
match their mental model at once).  *This* is the stuff for simple souls!

> Another good use of threads (suggested by Sam) is for GUI programming.
> An old GUI system, News by David Rosenthal at Sun, used threads
> programmed in PostScript -- very elegant (and it failed for other
> reasons -- if only he had used Python instead :-).
>
> On the other hand, having written lots of GUI code using Tkinter, the
> event-driven version doesn't feel so bad to me.  Threads would be nice
> when doing things like rubberbanding, but I generally agree with
> Ousterhout's premise that event-based GUI programming is more reliable
> than thread-based.  Every time your Netscape freezes you can bet
> there's a threading bug somewhere in the code.

I don't use Netscape, but I can assure you the same is true of Internet
Explorer -- except there the threading bug is now somewhere in the OS <0.5
wink>.

Anyway,

1) There are lots of goods uses for threads, and especially in the Windows
and (maybe) multiprocessor NumPy worlds.  Those would really be happier with
"free-threaded threads", though.

2) Apart from pedagogical purposes, there probably isn't a use for my "fake
threads" that couldn't be done easier & better via a more direct (coroutine,
continuation) approach; and if I had fake threads, the first thing I'd do
for me is rewrite the generator and coroutine packages to use them.  So,
yes:  you win <wink>.

3) Python's current threads are good for overlapping I/O.  Sometimes.  And
better addressed by Sam's non-threaded "select" approach when you're dead
serious about overlapping lots of I/O.  They're also beaten into service
under Windows, but not without cries of anguish from Greg and Mark.

I don't know, Guido -- if all you wanted threads for was to speed up a
little I/O in as convoluted a way as possible, you may have been witness to
the invention of the wheel but missed that ox carts weren't the last
application <wink>.

nevertheless-ox-carts-may-be-the-best-ly y'rs  - tim