Spring 91 - THREADS ON THE MACINTOSH
THREADS ON THE MACINTOSH
MICHAEL GOUGH
Threads are a great way to improve the performance and simplify the design of
programs. Apple's Advanced Technology Group developed a Threads Package to
implement this programming technique on the Macintosh. This article explains how
you can use this package to incorporate threads in your own code.
The idea for the Threads Package arose during the design phase of some scientific
visualization software, when we discovered that some of the applications we were
working on needed a way to juggle several simultaneous activities. It quickly became
clear that the Macintosh run-time environment posed some serious obstacles to anyone
wanting to implement threads on the Macintosh. With some effort, we were able to
come up with workarounds that made the use of threads with the Macintosh OS
relatively painless.
These workarounds are the main subject of this article. After briefly introducing the
purpose and mechanics of threads in general, the article presents some specific details
of the Macintosh threads implementation as it currently stands. A summary of the
functions in the Threads Package appears at the end of the article. The Threads Package
itself and several simple example programs can be found on the Developer Essentials
disc for this issue.
The Threads Package was developed as a means to an end, and it's by no means the last
word on threads for the Macintosh. We welcome any suggestions you may have for
improvements.
WHAT THREADS DO
Suppose you want to write an AppleLink ®-like communications program. You'd like
to write the program so that while it's downloading a file, it can also print an existing
file and allow the user to write a new message. A typical program can perform only one
of these functions at a time, displaying the watch cursor until the task is completed.
What's needed is some technique for allowing the program to perform these tasks
concurrently.
Programmers have often tried to achieve concurrency through the use of idle procs.
For your communications program, for instance, you could write the downloading,
printing, and text entry tasks as idle procs. While the download procedure is
executing, it could regularly call a printing idle proc to send a few lines of a message
to the printer. The download procedure could also periodically call an editing
procedure to allow the user to enter text for a new message in a window.
But think of the tremendous effort involved in writing the program so that it can
switch among these tasks. Every task would have to save variables each time it returns
so that it could resume where itleft off. Most complex functions would not be able to
contain deep levels of nesting because that would make it impossible to freely return to
the caller at any time. In fact, you'd have to divide most functions into inconveniently
small chunks so that you could juggle between them. The net result is that the
modularity of your program would be destroyed, and you'd have a tremendous
programming headache on your hands.
Threads are a much better technique for achieving concurrency than idle procs. When
your program uses threads, it's like a mind that can have several trains of thought
simultaneously. A program using idle procs, in contrast, is like a mind with a single
train of thought that must constantly interrupt itself to attend to side issues.
Note that there's a difference between multithreaded programs and multitasking
systems. Multitasking is the ability to run more than one application at once, but
each application can still only do one thing at a time. In other words, concurrency is
happening at the system level. A multithreaded application performs concurrent tasks
within the same program; concurrency happens at the program level. Of course, it's
possible to have a multitasking environment in which threaded programs run.
HOW THREADS WORK
When writing multithreaded code, you must let go of old ideas about how the machine
executes your program. Instead of a single program counter marching through your
code, in a sense you now have many. While the idea of multiple program counters may
sound complex, you don't have to relearn programming. You just need to be aware that
the main train of execution in a program is itself a thread and that all threads must
relinquish control to each other. You also have to remember to share globals and heap
objects that you used to access exclusively.
Here's a sample program that shows how simple it is to use threads. The program is a
modified version of the ever-popular SillyBalls. Unmodified, the program opens a
window and draws colored balls into it until the main event loop detects that the mouse
button is down. This new version forks a thread that beeps while the balls are being
drawn.
main()
ThreadHandle beepThread;
Initialize();
/* The InitThreads call initializes the Threads Package, converting
the original thread of execution into a swappable thread. */
InitThreads(nil, false);
/* This code forks a thread that beeps 30 times, and then quits. */
if (InNewThread(&beepThread, kDefaultStackSize))
for (i=0; i<30; i++)
{
SysBeep(120);
Yield();
}
EndThread(beepThread);
}
/* Here's the main event loop. The only change is the new call to