I was reading my email when I came across this blog about the death of single-threaded development. Now I've written many multi-threaded applications in C++ for applications, communications, and graphical displays, but I don't see the death of single-threaded development any time soon. I even wrote a paper while I was at university about how multi-threaded programming should be introduced in the curriculum much earlier. At the very least, the multi-threaded thought process should be taught but I don't see that happening.
Samples are usually distributed as single-threaded applications because they are easier to understand. Unfortunately people replicate the same style and methodology in their own applications. Until sample applications are distributed as multi-threaded applications, I do not believe single-threaded application will die or even that multi-threaded applications will outnumber single-threaded applications.
My First "Real" (as in Commercial) Multi-Threaded Experience
Personally, I'd love for more people to use multi-threading in applications. Unfortunately there is a significant learning curve where you progress from novice to expert. I remember one of my first multi-threaded applications involved reading communications data from a device asynchronously as the display updated the values. During QA, a bug was found if you clicked this dialog and pressed that button in just the right (or wrong) sequence you could crash the application. It turned out to be an issue where I forgot to suspend the communications thread before changing some key values. Actually, it wouldn't have crashed if there had been a semaphore in the communications thread to protect the resizing of an array.
The Classic Case of Debugging Multi-Threaded Applications
Another multi-threaded issue I had was a communications driver that has to run 24x7 on an embedded controller. There were four-plus threads: one for the application, one to monitor and handle configuration changes, one for the TCP/IP source data, and one for each of the configured serial ports. The source data thread read data from the host and then allow the serial ports to read the data. During the update of the source data, I protected the data with a class modeled after the Delphi TMultiReadExclusiveWriteSynchronizer. The application would mysteriously lock up after about 7 days and it was a particularly stubborn. Debugging crashes that occur only every 7 days takes a little while to find and fix. I suspected it was an issue with my scheduler (the component that controlled how quickly the threads execute or resume after completing an iteration). There was an issue with the scheduler early in the development where the tick counter could roll over (a DWORD holding milliseconds can count up to 49.7 days), but I knew that could be an issue from the beginning and fixed that quickly. After a good deal of debugging, I found the issue was with my TMultiReadExclusiveWriteSynchronizer class. I wrote a stress test application which hammered the synchronizer and sure enough found the problem. The application is still in production and running in many different plants around the US.
The Purpose or Need of Multi-Threading
In the case of the blog article mentioned at the beginning of this post, the author is promoting multi-threading for graphic and game algorithms but I believe it is broader than just graphic algorithms. I get frustrated when Outlook doesn't respond when it is downloading messages. Programming multi-threaded applications makes you a better programmer and a better debugger -- that is if you are successful at it. Some programmers are just not cut out for multi-threading applications.
Many web applications are moving to AJAX to provide a more seemless experience without a bunch of page loads. Unfortunately, some tasks still take a long time and should be handled differently. I modified a JobHandler (based on a DevX article) to support long-running tasks (BizTalk orchestrations, web services, communications, etc.). I added an Abort function, a watchdog timer, and general information like start and finish time, Session ID, and user name. In web-based applications, usually you must poll the JobHandler to see if it is complete. In OS-based applications, you can use a notifier or callback function to avoid polling.
There are typically two reasons for multi-threading: to complete a task quicker and to provide a seemless response. The first reason is most compelling due to the enormous performance gains that can be achevied. It is also the more difficult case to design more since you have to divide the tasks or calculations across multiple threads. The second case usually doesn't increase performance significantly, but the user appreciates the application more when it isn't frozen and not updating. At least in this case, you can monitor the thread, provide feedback, and attempt to abort it if necessary.
Common Issues with Multi-Threading
- Deadlocks -- classic issue, but also includes handling "hung" threads and using watchdog timers
- Scheduling threads -- figuring out how to run the threads quickly or pause the threads without overloading the system
- Notifications -- notifying the application or external systems about status or progress
Conclusion
Multi-theading is broader than just game and graphics programming, although the need for multi-threading in those areas is significant and absolute. Multi-threading increases your ability and experience, although you must expect challenges along the way.