(C) 2009 Hank Wallace
This series of articles concerns embedded systems design and programming, and how to do it with excellence, whether you are new to the discipline or a veteran. This article is about threading, why and why not to do it.
A thread is an execution unit within a larger program or system. One or many threads can run in a program, and some programs demand multiple threads (a seeming complexity) for greater simplicity. Some programs require only a single thread. Threads can run concurrently (on parallel processors), but generally run in a pseudo-concurrent mode through time slicing.
You know about threading, I understand. But do you know about the culture of threading? The culture of threading is the attitude that threading can and should be used in just about every program over 100 lines. Students do it, professors do it, engineers do it, so what’s wrong with threading?
The issue is that every tool is a safety hazard in the hands of a fool. Threading is no different. I cannot count the number of conference calls I have participated in where an engineer on the other end of the country said, “Just spin another thread to monitor that hanging API call, and if it hangs, kill the thread.” That band-aid eliminates all need for the guilty engineer to actually track down and fix a bug in his code, or the OS itself. One program had several instances of this thread kludge for hanging API and OS functions, all because the vendor was unmotivated to fix their problems.
And this threading tool, especially under Windows, is abused on a regular basis. The problem is that threads are easy to create, but nearly impossible to terminate externally. Sloppy programmers spawn threads willy nilly, then forget to terminate them, leaving nasties running in the background which hog resources. Sometimes programmers make a way to terminate a thread, but the never test the termination mechanism, so it works but not well.
Threads are great for handling asynchronous requests for services and peak demand tasks, and if used properly they can actually simplify a program. If you must use threads, please follow some guidelines.
- Only run a thread for as long as it is needed, then terminate it.
- Minimize thread count. Sloppy programmers spin up threads instead of designing one thread that does the work required in the proper way.
- Build into each thread a kill switch so that it may be terminated from the main thread.
- Test the termination conditions and commands for each thread.
- Use a kernel tool to determine what threads your program spawns.
- Be sure your program accesses data in a thread safe way.
- Have failsafes for logic that depends on signals from other threads, in case those threads crash, such as timeouts.
- Log activity with an indication of which thread produced the information.
- Have a good understanding of the sequence in which your threads should be started and stopped.
If you can use one or more state machines to service system requests and execute program logic, that is sometimes the better option in that it is simpler, inherently thread safe, and easier to debug. It is quite difficult following the logic of synchronized threads spread among multiple source files. See the article Using State Machines In Your Designs.
Some programmers spawn threads as often as K&R called functions. This is silly. If you are finding yourself spawning threads in every 100 lines of code, you need to rethink the design of your program. You can do a lot of work with some simple state machines called in a loop.
Hank Wallace is the owner of Atlantic Quality Design, Inc., a consulting firm located in Fincastle, Virginia. He has experience in many areas of embedded software and hardware development, and system design. See www.aqdi.com for more information.