When I was a boy, threads were called interrupts.

Earlier this evening I gave my dad a birthday card that said "Celebrating the year you had to walk 8 miles through the snow to be born." Having grown up hearing my fair share of "When I was a boy..." stories that all pointed out how easy I had it compared to what he had to deal with growing up, I thought it was kind of funny. Then tonight, I found myself responding to a frantic plea from someone that needed help tracking down some issues in their multithreaded forms application. I won't go into the details of what I found when I looked into things, but I'm fairly certain they'd have no trouble qualifying for Fox's next episode of it's new high tech reality show, "When Threads Attack". But this little episode (which is not uncommon in my line of work) got me thinking. How on earth could these people not get this stuff working with all the cool things .NET gives us in the async space - like integrated thread pools, 4 letter keyword-based locking, and named threads? Why, when I was a boy...

About 12 years ago, I was building software that connected DOS applications to fax devices that were either connected to the PC via a bidirectional parallel port or reachable across a Novell network. So I was working on modern stuff like DOS device drivers and TSRs that were loaded at PC startup via config.sys and autoexec.bat. Asynchronous execution was achieved by hooking real mode interrupt 8 (the PC clock), and doing work a slice at a time; paying careful attention to the preservation of registers. This allowed us to send and receive faxes in the background w/o interfering with the normal operation of whatever program the user was interacting with (eg: Wordstar). The programming language of choice was Intel x86 assembly, and we debugged things by hooking up an oscilloscope to the serial port. Each routine in the ISR wrote a different binary value to COM1, "lighting up" different pins on the port. The varying patterns that resulted on the scope told me where my code was/wasn't and what it did last before choking. Sure, some of my Unix friends had pthreads and got to program in C, but that was for sissies that couldn't handle "real" async programming.

Not long after that, I left for the greener big company pastures of Intel to work on realtime conferencing systems for the PC platform. By now, Windows 3.0 had gained a lot of momentum but, unfortunately for us, didn't support pre-emptive multithreading (a distinct step backwards from DOS as far as I was concerned :-). Luckily for us, a group of really bright people upstairs in the research labs had ported a realtime kernel to Windows as a set of VxDs. This kernel, which completely took over Windows during boot, provided us with a preemptive scheduler that supported the notion of prioritized "tasks". Windows itself became the kernel's idle thread. So only when all of our tasks settled down did Windows get to run. This was ideal for an audio/video conferencing system, because it meant that PC users couldn't cause our communications subsystems to lose their connections by simply grabbing the mouse and dragging a window around the screen like they were doing before we discovered the kernel upstairs.

Having evolved myself by now from assembly to C to C++, I was feeling my oats as an OO programmer and decided that I couldn't live without C++ in kernel mode. Just imagine what I could do with a C++ Task class that abstracted away the minutae of multitasking in kernel mode! Trouble was, the compilers and linkers didn't support C++ development of VxDs at the time (this was before products like VtoolsD were invented), so I felt compelled to write the necessary startup code and supporting new/delete/etc operators that enabled me to introduce C++ to VxD land. This was wonderful, except that the kernel mode debuggers at the time only supported C. But that was a small price to pay in exchange for having a C++ Mutex class.

Eventually, Windows NT shipped and I graduated to Win32's version of multithreading. Finally - Windows had caught up with the rest of the world by offering a preemptively scheduled multithreaded world for asynchronous programming. Between that and honest to goodness virtual memory, I was in async heaven. Microsoft had brought multithreading to the masses - providing cool functions like CreateThread, CreateMutex, and InitializeCriticalSection. What could be simpler? Of course, I was a little worried that since Microsoft had just made multithreading so accessible to the general developer community, that my job security had taken a hit. Surely everyone would start building highly scalable, responsive applications now.

It wasn't long, however, before I found myself spending less time writing my own code and more time wandering around from cube to cube helping other developers fix multithreading bugs in their parts of the system (at one point, the project I was working on had something like 200 programmers involved). In an effort to make multithreading a little more approachable, I wrote various versions of thread synchronization wrapper classes (like CMutex, the new and improved replacement for Mutex) and a custom thread pool (to address the thread mania mess my colleagues had gotten themselves into). Things progressed slowly at this point; the only highlights being NT 3.51's introduction of the I/O completion port, and Windows 2000's introduction of a built-in threadpool. At some point in here, MFC graduated to Win32 and provided its own CMutex and highly value add methods like AfxBeginThread. But I was never much of a button boy, so I tried to ignore MFC except when colleagues needed help with multithreading in their MFC apps.

And who could forget COM? At some point during all of this, COM came along and gave birth to a wonderful construct known as the apartment. I couldn't believe how easy Microsoft was making it for programmers to "do threads" right. Here was a platform that let you build a component without worrying about what the thread-savvy application developers were doing around you, and just use a little registry setting to tell COM they had to deal with it for you. How much easier could it get?

Of course history (and lots of therapy bills) subsequently proved that multithreading was still too difficult to get right for the bulk of the developer community. But then .NET came along...

With .NET, Microsoft programmers were provided with the warm and inviting pink padded cell of the CLR within which they could finally practice safe multithreaded programming. Here was a runtime that provided a builtin thread pool with a type-safe interface in the form of Delegate.BeginInvoke - the magical partnership between the compiler and runtime that was destined to make put multithreading within easy reach of programmers everywhere. Microsoft even provided a multi-reader, single-writer lock for us! And if that weren't nice enough, the Windows Forms team was kind enough to provide us with ISynchronizeInvoke - a work around for the age-old problem of Windows having thread affinity (although they lost points for giving its members names like BeginInvoke and Invoke, which are not to be confused with the same-named members of the Delegate class).

Which brings me to tonight. After all this time, why is it that multithreading is still so difficult to get right for most programmers? I could waste lots more of your time discussing the answer in the small, but I realized tonight for the first time that the biggest barrier to "doing threads right" is letting programmers touch them.

Think about it - web application developers everywhere routinely build scalable, responsive systems that leverage the wonders of things like I/O completion ports, threads, and pooled resources. But they do it without (in many cases) even realizing those constructs are involved. ASP.NET/IIS/http.sys handles the async I/O and thread pooling necessary to efficiently pull a request off the wire and dispatch it into the application. The ASP.NET application developer then fiddles around with some stuff in memory (like strings) and proceeds to leverage a pooled connection to issue a query to a database, where something like SQL Server again uses async I/O and fancy threading techniques to formulate and send back a query result. At this point, the app developer then happily binds to a control before letting ASP.NET/IIS/http.sys again use async I/O to write the response buffer back to the client.

ASP.NET applications are the only successful examples of (Microsoft-based) multithreaded applications being developed by the general purpose Microsoft developer community. And it happened because ASP.NET takes multithreading out of our hands. Hmm...

I've been side tracked lately working on speech applications for Microsoft Speech Server, so I haven't had much of a chance to dig into Whidbey to see whether Microsoft has brought ASP.NET's pay-no-attention-to-the-thread-behind-the-curtain approach to multithreading to the desktop forms-based application. It might be there. But if it's not, I sure hope they're working on it for Longhorn.


Posted May 26 2004, 08:08 PM by mike-woodring

Comments

Scott Allen wrote re: When I was a boy, threads were called interrupts.
on 07-26-2004 11:05 AM
It looks like it *might* be getting there in Longhorn: http://www.simplegeek.com/PermaLink.aspx/35828500-54d8-4e66-acc4-217ef9440322
Nick Parker wrote re: When I was a boy, threads were called interrupts.
on 08-02-2004 1:24 PM
Would it be safe to say that too many developers out there are trying to write multithreaded applications when they really don't know exactly how to do it correctly from the get go? Your story depicts Microsoft slowly identifying this portion of the development community and their only defense mechanism is to shield the developer from such tasks.
Q wrote re: When I was a boy, threads were called interrupts.
on 09-12-2004 11:41 AM
Doesn't even Hotmail reports 'Too busy'..

Somehow fail to see multiple, relatively long-running (1-10sec) DB queries handled efficiently with the 'abstracted' approach.. The data return is synchronous, tie the queued/waitig thread/handles/stack/VM. DB(s) getting bog down by load eventually first, then IIS is it not?

Also seem to recall SQL connections breaking down on few hundred/sec OLEDB-based SP calls few years back..

Not that it is necessarily common to hit real hard, but don't concurenncy issues just move onto the next and even more dangerous plane?
Michael wrote re: When I was a boy, threads were called interrupts.
on 10-04-2004 6:11 PM
Great History lesson.
plumsauce wrote re: When I was a boy, threads were called interrupts.
on 11-14-2004 10:56 PM

Must be the fax thing. I was doing fax and telex software at about the same time you were, using the same tools and the same platform. And I have zero problems with multithreading. Didn't get to work for Intel or Microsoft though :(

Last night I was reflecting on the fact that multithreaded programs are actually easier to conceptualise because they are by necessity broken into discrete chunks with well defined boundaries.

The problem in all systems work is that there is a limited population who 'get it', that is the reason there is such a push for OO abstraction. There was never anything wrong with assembler. Business needs more 'talent' than the world can possibly supply. Therefore, the level keeps on dropping to try to create supply.


BTW, I arrived here looking to figure out how to force http.sys into Win2K to get at the http api without going to Win2K3.

Mark Eichin wrote re: When I was a boy, threads were called interrupts.
on 12-10-2004 9:04 PM
Similar time period, I was at HP/Apollo working on Aurora (bits of which supposedly became CORBA) and we learned the hard way that, at least with C++, the only developers that "got" threads had either kernel or embedded-systems experience. Pretty much everyone else would hurt themselves as soon as they got past Hello World... modern language integration may be overprotective, but it is making threading a lot more accessible.

As you said, the problem is "letting programmers touch them."...
Jay Lauffer wrote re: When I was a boy, threads were called interrupts.
on 12-11-2004 5:26 AM
Multi-threading entails some of the most significant concepts in computing: resources and processor time. Many developers tend to overcomplicate a multithreaded application, because they get so excited about I'll do this and I'll do that, in today's environment especially in areas like managed code priority isn't given to the limitation of resources, nor are people concerned about room or space. I tend to suspect that a simple lack of understanding about how a processor works and weak fundamentals in hardware architecture leads to some of the nasty problems developers cook up when they start adding additional threads. The idea that A and B will both just happen is convenient and easy to conceptualize, the reality is that two things don't occur at once, everything is sequential, obviously technology has changed and the illusion of two things occuring simultaneously exists. In the case where you are trying to emphasize to developers (younger ones like me for example) about the nature of resources and contention try getting them all in a room, start simple with one red hat and one blue hat. The red hat owner gets to ask a question and the blue hat owner can answer. Let them work out for themselves how to follow these rules.
Rick Strahl wrote re: When I was a boy, threads were called interrupts.
on 04-28-2005 4:40 PM
Wrappering multi-threading on server applications makes perfect sense, but how would you accomplish the same thing on the client side? There are very common repeatable scnearios on the server, but in Windows Forms apps there are no such easy points that you can identify and build a wrapper for.

Ultimately, somebody has to write the multi-threaded code, and it's up to the tool vendor to provide the highlevel implementations. I'd think it would be really nice if many common classes that require multi-threading scenarios when applied automatically performed that sort of thing for you and then just fire events that you can handle in the background. This is not the case in .NET today - we're one level removed from that. At the same time in .NET at least it's fairly easy to do this yourself certainly a heck of a lot easier than COM threading and understanding how the different thread marshallers work there (yuk)...

the biggest bottleneck is Windows. In my own multi-threaded apps, the problems I end up fighting with are dealing with sending messages properly synchronized to the UI. Again, .NET helps with Control.Invoke/beginInvoke, but this is messy because of the generic eval parameter interface.

As you say the trick to successful multi-threading apps is to keep it simple with keeping minimizing the thread boundary crossings to single interface points that can be easily synchronized (if necessary) and debugged. Easily being relative here <g>
Daniel Skipper wrote re: When I was a boy, threads were called interrupts.
on 12-01-2005 1:20 PM
A very good read. I'm a new(ish) C++ programmer and am slowly discovering the worlf of threading. In most of my code so far I have managed to avoid problems by having each thread very tightly definied in terms of what it is and what it is not allowed to do.
I don't like bubble wrapping everything so I can't hurt myself. As far as I can see most of the advantages of programming in C++ (or C for that matter) is that it is lower level and faster than many languages, if everything is wrapped isn't this sacrificed? If I want evrything in nice little packages I'll code in Java or Python or other so called "high level" languages.
It's much more fun to pipe data around a program and mutex just about every system call.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?