Who would ever write a multi-threaded GUI program?


During the development of Windows 95, the user interface team discovered that a component provided by another team didn't work well under multi-threaded conditions. It was documented that the Initialize function had to be the first call made by a thread into the component.

The user interface team discovered that if one thread called Initialize, and then used the component, then everything worked great. But if a second thread called Initialize, the component crashed whenever the second thread tried to use it.

The user interface team reported this bug back to the team that provided the component, and some time later, an updated version of the component was delivered.

Technically, the bug was fixed. When the second thread called Initialize, the function now failed with ERROR_NOT_SUPPORTED.

The user interface team went back to the team that provided the component. "It's nice that your component detects that it is being used by a multi-threaded client and fails the second thread's attempt to initialize it. But given that design, how can a multi-threaded client use your component?"

The other team's reply was, "It doesn't matter. Nobody writes multi-threaded GUI programs."

The user interface team had to politely reply, "Um, we are. The next version of Windows will be built on a multi-threaded shell."

The other team said, "Oh, um, we weren't really expecting that. Hang on, we'll get back to you."

The idea that somebody might write a multi-threaded program that used their component caught them by surprise, and they had to come up with a new design of their component that supported multiple threads in a clean way. It was a lot of work, but they came through, and Windows 95 could continue with its multi-threaded shell.

Comments (37)
  1. Joshua says:

    There's a pretty short list of components that meet this criteria. Might have well have named the offending component.

    [You're assuming the component is not an internal one. -Raymond]
  2. Henke37 says:

    I duno, a "Component" could be anything. And the identifier "Initialize" is very common. The requirement and age could help limit the search a lot, but it is still a rather large search range.

    One could assume that "Component" has the requirement of being a visible control, especially when it is the UI team involved and the discussion is about multi-threaded GUI programs. But that is not a safe assumption, they could have been using any old "component".

  3. poizan42 says:

    Well, the WPF developers still seems to think that…

  4. Mike says:

    Technically, Microsoft's major UI platforms (WPF, Silverlight, WF) are still single-threaded GUIs. The business logic and background services might be multi-threaded, but most interaction with the UI layer needs to be marshalled back to its primary, and single, thread.

  5. Daniel says:

    Well "theoretically" GUI app's can be done with Multi-Threading. But honestly: short of a few well-defined worker threads you better not touch that territory unless you REALLY have to.

    (Just talk about "events" that get raised by background threads and like to update the GUI… Asynch events anyone?).

    Only thing that works is if you design Multi-Threading into the whole thing right from start (and have some decent framework support for it)

    Note: It doesn't make things easier that I'm working with Delphi and VCL (The Delphi component library) never ever heard of multi-threading GUI…

  6. Paul Topping says:

    Given the difficulty of multi-threaded programming and its testing, cooperative multi-tasking within the app is a better way to go. With good infrastructure for doing idle-time processing the problem ends up being mostly a matter of making sure one's code breaks long tasks into small pieces. I am not saying it solves all problems though but most of them.

  7. alegr1 says:

    First rule of multithreaded GUI programming: Don't do multithreaded GUI programming.

    First rule of single-threaded GUI programming: don't have multiple top level (unowned) windows in a single thread.

    Having multiple threads to run multiple top level windows is the only good reason for multithreaded GUI.

    Worker threads are OK, as long as you know what you're doing.

  8. John says:

    These are my favorite blog posts :)

  9. Ancient Hacker says:

    it's not just Windows, this was a big problem in the old Mac OS.

    The Quickdraw routines assumed there would be only one stack and that if Quickdraw wanted to do something memory-intensive, like take the intersection of two regions or BitBlt, it could merrily grab all the free stack space, partition it into thirds, and use all that space for source, temp, and destination regions or bitmaps.  That didn't work too well when you put the GUI into a thread with say 8K of stack space.

  10. Joker_vD says:

    Wow. Say "multi-threaded GUI", and watch people getting sad. Well, what would you expect, of course multiple mutually-dependent actors are hard to reason about. That's why social interactions suck, you know. Being the one, united and whole, is much simpler.

  11. Dominic says:

    "average GUI program uses 1.2 threads" factoid actualy just statistical error. average GUI program uses 1 thread. Chicago Shelly, who lives in protected mode and uses over 20 threads, is an outlier adn should not have been counted

  12. Gabe says:

    Dominic: A quick sampling of my system showed Solitaire as the only running GUI app with a single thread. IE, Explorer, VS, all have dozens of threads. WinWord has maybe 10.

    I tried opening Calc, Paint, Notepad, and WordPad to make some more single-threaded GUI apps and was surprised to see that Notepad was the only one with a single thread. Even Calc has 3 threads! I can see why WordPad might have 3, but I can't imagine what Paint is doing with 6.

  13. alegr1 says:

    Shell is multithreaded because it needs to have multiple top-level windows. Having multiple top-level windows belonging to one thread is a recipe for disaster.

  14. alegr1 says:

    @Gabe:

    GDI+

  15. ThreadPool.QueueUserWorkItem, Control.Invoke, and the Control.Begin/EndInvoke pair are things you become intimately familiar with when you're writing GUI apps in C# and you don't want them to respond like *** (the later methods being because of the whole "oh, you want to update that UI element?  You'll have to do it on the thread that actually owns it" thing).

  16. xor88 says:

    I believe the User subsystem has primarily been made thread-safe to avoid security issues. We don't want to be able to manipulate the Kernel by causing race conditions.

    The user-mode GUI frameworks do not need these security properties so they just declare to be single-threaded.

  17. DanielBryars says:

    I wonder, would it have been more work to do multithreading from the get go? Without a working single threaded version would there had even been the demand for this component in the new shell?

    How do you draw a line between only coding for the absolute requirements of the moment and generalising too far?

  18. Cheong says:

    @DanielBryars: It depends only how you believe the others will use your component.

    For the component in the story, since it's originally thought that it will only be used in single threaded application (maybe the component existed the days of Win3.X where att you have is cooperative multitasking), it's okay to design like that.

    If later you found out otherwise, you change it just like what it had been in the story.

    Btw, multithreading means you have to deal with race condition and deadlock. By the nature of your component it could be quite some work to get headache with.

  19. 640k says:

    Then why was it recently argued by Raymond that USER controls and common controls wasn't thread safe?

  20. Ian Boyd says:

    Isn't it fundamentally a rule that the only thread allowed to touch a window is the thread that created it? E.g. any thread could create a ProgressDialog control. But no matter how much synchronization you apply, the only thread allowed to use that control is the thread that created it.

    Although ProgressDialog is a bad example, since it is a COM object

    And COM inter-apartment rules come into play.

    But if I create a child window (eg a RichEdit), are threads other than the creator thread allowed to send it messages? Are threads other than the creator thread allowed to send it messages?

  21. Joker_vD says:

    @xor88: Guess what, bochspwn is a thing. There are security-affecting races inside the kernel.

    @cheong00: One has to worry about races and deadlock not only when multi-threading, but pretty much every time it interacts with the outer world.

    @Ian Boyd: Yes, that's the rule. That's why when I close the DJVU reader on my Android phone and then re-open it some time later, I am greeted with "Only the original thread that created a view hierarchy can touch its views" error message, and it then the reader closes.

  22. Leonardo Herrera says:

    I don't understand the need to point out that the current UI model requires one's application to get back to the main thread. I've always assumed that the shell *is* multithreaded. Am I wrong?

  23. Bill P. Godfrey says:

    Multi-threaded GUI? I just write Application.DoEvents(); every other line. So much simpler.

    :)

  24. nikos says:

    @alegr1  

    "Having multiple top-level windows belonging to one thread is a recipe for disaster"

    how do you substantiate this statement? I do many top level windows for a single thread (eg modeless dialogs but not just that) and didn't see any problems so far

  25. Daniel Rose says:

    @Mike "Technically, Microsoft's major UI platforms (WPF, Silverlight, WF) are still single-threaded GUIs."

    Actually, that is not quite true. All interaction with a UI element needs to be done on the thread which created it. But there is nothing to stop you from creating two top-level windows in two different threads (I do this in my application).

    Some third-party controls/helper libraries do not work properly if this is done, however.

  26. Neil says:

    @Ian Boyd: Sent messages were covered almost exactly 10 years ago: blogs.msdn.com/…/150929.aspx

  27. xor88 says:

    @Joker_vD yes, those are security bugs. By design User is thread-safe in the sense that you cannot make it elevate your privileges. It might still behave erratically, of course. Not sure what thread-safety guarantees Users makes to callers.

  28. Ben Voigt says:

    Ian asked "But if I create a child window (eg a RichEdit), are threads other than the creator thread allowed to send it messages? Are threads other than the creator thread allowed to send it messages?"

    Yes, in Win32 you are.  The restriction that all interaction with a window must originate on the creator thread is a "feature" of frameworks such as .NET WinForms.  The underlying Windows API synchronizes access from other threads quite nicely.  Most requests need to invoke the window procedure; these are marshalled to the creator thread.  But some such as (cross-process) `GetWindowText` or `GetWindowLong`(Ptr) just access internal thread-safe data structures.

  29. voo says:

    People again and again try to create thread-safe GUI frameworks and in the end the problems you have with those just aren't worth the effort – from Cedar at Xerox to awt.

    The current event queue system that is only thread-safe for a few specific methods seems to be working reasonably well

  30. alegr1 says:

    @nikos:

    It's all fun and games, until one top-level window hits a modal loop (for example, opens a modal dialog). Then any menu commands of other top level windows will be executing in the context of a foreign message loop, which may not have the necessary on-idle handling, for example.

  31. KC says:

    I always thought the single-threaded rule was a Win32 limitation, that there is only 1 message queue and all Window messages have to go through that queue.   If you have multiple top-level windows, do they each have their own message queue?   Does WM_QUIT to one window leave the other alive?

    BTW, I've seen the same limitation on every windowing system I've encountered.

     KC

  32. SimonRev says:

    You can try to manipulate your UI from multiple threads in Win32.  Once you try you will VERY quickly understand that there is a very good reason WinForms, Silverlight, WPF, etc all enforce a the rule that all access to a UI element must occur in the thread that created it.

    It is nearly impossible to avoid deadlocks if you have multiple threads manipulating the UI of a single window as your SendMessages (or more likely macros that silently wrap SendMessage) block on the UI thread.  The one or two times I have tried it, I very quickly changed my approach and created a quick and dirty Invoke like mechanism which solved all of those problems and made the program much easier to reason about as well.

  33. Cheong says:

    @alegr1: Agreed.

    Not to mention that many shell extension runs in Explorer, and Explorer do need to wait for these component to response. If all Explorer window belongs to single thread, Explorer should choke here and there, much worse than the Flash player we have now. (I sincerely hope they give us option to run seperated instance on different browsers, so when one Flash applet for some reason runs under tight loop, I don't have to kill my IE, Firefox and PCMan(which loads IE as ActiveX plugin) at the same time because they all goes unresponsive)

  34. Daniel says:

    Short notice about "SendMessage" from other threads: DON'T!! (SendMessage expects that the user thread can process the message and return an answer. So you're on the road to deadlock…)

    PostMessage on the other side is OK (and is my preferred way to communicate results from a worker thread to a user thread). You're restricted to "write" messages though (e.g. no result, and no "out" or "in/out" parameters like pointers to structs that get modified etc…)

  35. Mike Dimmick says:

    @KC: Each *thread* has its own message queue. All windows created on that thread share that message queue. WM_QUIT is not a message to a window and should never ever be sent or posted with PostMessage. WM_QUIT is what GetMessage returns after PostQuitMessage has been called, and that quit flag is a property of the thread, not of any window.

    You'll note that PostQuitMessage doesn't take the handle of the thread, nor a handle to a window. It's supposed to be called from a window procedure in response to some other message, and posts the quit flag to the message queue for the thread that called it.

  36. John Doe says:

    I don't understand all the hate towards multi-threaded GUIs.  The only thing that keeps most GUI systems single-threaded, or even main-threaded, is thread local storage, and in the second case, TLS that gets initialized once only before main() is called.

    Win32 has one GUI thread rule, that is to handle a window in the thread it was created.  It seems to have been a decision between process-wide or thread-wide message queues, and thread-wide won because it's more logic e.g. you can stuff your own TLS data to go with a thread's message queue, it's more flexible (multiple top-level windows, multiple STA threads which underlying mechanism is none other than a windowing message queue), and it was something that was either done right when threads were introduced or never.  That decision was all about profit for all.

    I agree single-threaded GUI is the most simple one, but it should not be the only one.  There are a handful of cases where you (wish you) could use multiple threads for different windows.

  37. immibis says:

    Why does everyone keep saying that you need a multi-threaded GUI to have multiple top-level windows…?

Comments are closed.