Pros and Cons

Cheap Threads eliminates the worst drawbacks of other kinds of multitasking. Instead, it offers a different set of drawbacks. Take your pick.

Multiple Processes

Multiple processes may work well when the different tasks don't need to communicate much among themselves, or when the data flows in only one direction. A UNIX pipeline, for example, may be implemented as a one-liner in a shell script. Each process may be developed and tested in relative isolation from the others.

Where the processes are more tightly coupled, the developer must wrestle with whatever facilities are available for interprocess communication. These facilities are seldom simple and never fully portable.

Some environments, such as MS-DOS or some embedded systems, don't support multiple processes. In that case you must resort to some form of multithreading.

True Threads

By "true threads" I refer both to kernel threads and to user threads. From a design standpoint they are comparable. User threads are typically more efficient at task switching, but kernel threads can take advantage of multiple processors. The choice between them will generally depend on the details of what's available.

Instead of various mechanisms of interprocess communication, true threads use various mechanisms of interthread communication, which may be simpler or incur less overhead. However they are also more subtle. Because different threads may access the same memory at unpredictable times, it is easy for them to interfere with each other in ways that are difficult to reproduce or debug.

With true threads, you must allocate a chunk of memory for each thread to serve as a working stack. Typically, at any one time, much of that memory is unused, depending on where the threads are in their respective calling chains. The resulting waste of memory can be considerable, especially for large numbers of threads.

When the runtime system jumps from thread to thread, it must also jump from stack to stack. There is no way to do so in Standard C or C++. Unless you use a language such as Java that supports multithreading directly, true threads are inherently non-portable.

Cheap Threads

Because Cheap Threads is written in Standard C, it is usable in any environment where a hosted implementation of Standard C is available. It requires no special facilities from the operating system, nor from proprietary libraries. Embedded Cheap Threads is intended for stand-alone implementations of C. You may have to provide your own versions of certain standard library functions such as memcpy.

Because Cheap Threads is open software, you can tinker with it to suit your needs, subject to the terms of the GNU Lesser General Public License. For example, you might eliminate features you don't use, in order to reduce overhead. You might convert it to C++ or K&R C, or add a new feature. I'd like to think you'll never need to debug it, but you can if you have to.

Because Cheap Threads is entirely synchronous, it avoids the subtle dangers surrounding true threads when two or more of them access the same resources. You don't have to use semaphores, mutexes, or any of their kindred.

Cheap Threads does not allocate a separate stack for each thread, and is therefore sparing in its use of memory. As it jumps from thread to thread, it reuses the same stack used by the other threads. Each thread is responsible for saving its own state between invocations, by whatever means necessary.

It is always risky to make predictions about performance without doing the appropriate benchmarks. Nevertheless it is likely that Cheap Threads will incur less overhead than other forms of multitasking. The use of multiple processes involves many context switches, which typically are relatively expensive operations. The task switching used in true multithreading may or may not incur much overhead, but the use of mutexes and the like incurs overhead that doesn't apply to Cheap Threads.

Finally, Cheap Threads is available at no charge. You can't get much cheaper than that.

Now for the drawbacks.

As noted earlier, a long and complicated task may need to be broken up into smaller pieces. This style of coding is likely to feel awkward and unnatural. It also incurs overhead of its own, as the thread repeatedly navigates in and out of whatever it's doing.

Because the scheduler never interrupts a running task, it is possible for a long-running thread to use up more than its fair share of the CPU, or to get trapped in an endless loop. Several mechanisms give the developer some control over the scheduling, but they cannot promise more than an approximate balance among the threads.

Non-Blocking IO

One of the most common reasons to use multitasking is to perform non-blocking IO. While one thread waits for a chunk of data to arrive from the disk drive or other device, other threads can be working instead of just waiting. The result is greater throughput, especially for a server process with multiple clients.

For that purpose, Cheap Threads has nothing to offer. If one thread waits for IO, they all wait.

On the other hand, you may be able to implement non-blocking IO by other means, using (for example) calls to low-level interrupt routines. In that case Cheap Threads may provide a useful way to organize whatever the application does while it waits for the IO to complete.

Summary

Cheap Threads is worth consideration under the following conditions:
Home