views:

159

answers:

4

Or, equivalently, how would you design such an API. Expected/example usage would be illustrative as well.

My curiosity comes directly from the comments (and subsequent editting on my part) of this answer. Similar questions/discussions in the past provide a bit of inspiration to actually asking it.

Executive summary:
I don't feel a multithreaded UI api is possible in a meaningful way, nor particularly desirable. This view seems somewhat contentious and being a (relatively) humble man I'd like to see the error of my ways, if they actually are erroneous.

*Multithreaded is defined pretty loosely in this context, treat** it however makes sense to you.


Since this is pretty free-form, I'll be accepting whichever answer has the most coherent and well supported answer in my opinion; regardless of whether I agree with it.

Answer Accepted


**Ok, perhaps more clarification is necessary.

Pretty much every serious application has more than one thread. At the very least, they'll spin up an additional thread to do some background task in response to a UI event.

I do not consider this a multithreaded UI.

All the UI work is being done on single thread still. I'd say, at a basic level, a multithreaded UI api would have to do away with (in some way) thread based ownership of UI objects or dispatching events to a single thread.

Remeber, this is about the UI api itself; not the applications that makes use of it.

+2  A: 

I don't see how a multithreaded UI API would differ much from existing ones. The major differences would be:

  • (If using a non-GC'd language like C++) Object lifetimes are tracked by reference-counted pointer wrappers such as std::tr1::shared_ptr. This ensures you don't race with a thread trying to delete an object.
  • All methods are reentrant, thread-safe, and guaranteed not to block on event callbacks (therefore, event callbacks shall not be invoked while holding locks)
  • A total order on locks would need to be specified; for example, the implementation of a method on a control would only be allowed to invoke methods on child controls, except by scheduling an asynchronous callback to run later or on another thread.

With those two changes, you can apply this to almost any GUI framework you like. There's not really a need for massive changes; however, the additional locking overhead will slow it down, and the restrictions on lock ordering will make designing custom controls somewhat more complex.

Since this usually is a lot more trouble than it's worth, most GUI frameworks strike a middle ground; UI objects can generally only be manipulated from the UI thread (some systems, such as win32, allow there to be multiple UI threads with seperate UI objects), and to communicate between threads there is a threadsafe method to schedule a callback to be invoked on the UI thread.

bdonlan
A: 

Most GUI's are multithreaded, at least in the sense that the GUI is running in a separate thread from the rest of the application, and often one more thread for an event handler. This has the obvious benefit of complicated backend work and synchronous IO not bringing the GUI to a screeching halt, and vice versa.

Adding more threads tends to be a proposition of diminishing returns, unless you're handling things like multi-touch or multi-user. However, most multi-touch input seems to be handled threaded at the driver level, so there's usually no need for it at the GUI level. For the most part you only need 1:1 thread to user ratio plus some constant number depending on what exactly you're doing.

For example, pre-caching threads are popular. The thread can burn any extra CPU cycles doing predictive caching, to make things run faster in general. Animation threads... If you have intensive animations, but you want to maintain responsiveness you can put the animation in a lower priority thread than the rest of the UI. Event handler threads are also popular, as mentioned above, but are usually provided transparently to the users of the framework.

So there are definitely uses for threads, but there's no point in spawning large numbers of threads for a GUI. However, if you were writing your own GUI framework you would definitely have to implement it using a threaded model.

patros
If the UI is manipulated on one thread only, it can't really be multithreaded can it?
Kevin Montrose
If you've got a rendering thread, a dispatcher thread, an animation thread, an event handler thread and a pre-caching thread then would you not consider that multi-threaded? If so your definition is too narrow.
patros
Care to illustrate how rendering and animation can be divorced? Or dispatching and event handling? Or how either thread can be run safely simultanously? I'm not trying to be snarky, its just that you can split your code into n-threads with n-names and then lock-convey them into what is effectively a single thread.
Kevin Montrose
A good example is sliding animations. You want to move something by N pixels per second, so you spawn another thread. The thread attempts to sleep for say 50ms. Due to scheduling, this may well be 100-200ms. Thread wakes up, checks the actual time elapsed and moves the object time/N pixels. This way, the only contention is on the coordinates of that one object, and since the only action is a simple get/set of the coordinates, contention is minimal.
patros
As for dispatchers/event handling, this is actually implemented in a lot of toolkits. AWT comes to mind. Obviously a mouse-event shouldn't preempt whatever code is currently executing, so the event handler thread puts it onto a queue of its own. The main thread polls the event handler, and processes as many items from the queue as it can between rendering frames. I've written similar systems for non-system events. The main thread wont even block, just attempt a lock on the queue. If it's already locked, it will break out and render another frame before trying to lock the queue again.
patros
Sliding animations work great, until you animate two properties on the same logical object; then the animation falls apart due to the timing issues. AWT event handling is single threaded; all events are handled on a single thread, if you run your code directly in response to an event it will delay the next event being handled. Arguing about the model of AWT is also kind of iffy, as its built on top of other, purely single threaded, UI apis.
Kevin Montrose
Upon further investigation, AWT repaint requests also go into the event queue. Thus rendering shouldn't happen when an event is being handled either.
Kevin Montrose
It's been a couple of years since I used AWT, so I don't remember the specifics. I'm fairly certain though, that the event handling code is not called directly by the thread that handles the IO.As for the animations, you're arguing against a strawman . If you're changing two (or more) properties of the same object, then you create composite animations or have the same thread handle all of the animations on a single object.For the sake of argument, try to imagine the best implementation of a threaded UI model you can and only poke holes in that.
patros
If you can find a source for AWT rendering on a seperate thread, that'd be great. Everything I can find says rendering occurs in response to PaintEvent(s) which come off the singular EventQueue running off a single thread. The only multithreaded-ness AWT has is that components will try to acquire a UI-lock if they are modified on the "wrong" thread; which leads to some notoriously deadlocks. As for the animating thread, I fail to see how it doesn't devolve into a lock-convoy once you get n > 1 on animations; most animation libs deal in [start, end, time] tuples, that are updated on repaint.
Kevin Montrose
A: 

There is nothing wrong with, nor particularly special about multithreaded ui apps. All you need is some sort of synchronization between threads and a way to update the ui across thread boundaries (BeginInvoke in C#, SendMessage in a plain Win32 app, etc).

As for uses, pretty much everything you see is multithreaded, from Internet Browsers (they have background threads downloading files while a main thread is taking care of displaying the parts downloaded - again, making use of heavy synchronization) to Office apps (the save function in Microsoft Office comes to mind) to games (good luck finding a single threaded big name game). In fact the C# WinForms UI spawns a new thread for the UI out of the box!

What specifically do you think is not desirable or hard to implement about it?

Blindy
If you still do all the UI manipulation on a single thread, its pretty singlethreaded from the UI perspective. Applications are obviously multithreaded in many cases, and rightly so.
Kevin Montrose
Depends what you mean by that, the GetMessage/ProcessMessage loop obviously runs on a single thread, but the actual updates can be originated from any number of threads then marshaled across thread boundaries.
Blindy
Yes, but the final update to the __UI__ originates from a singular thread, regardless of which thread originally decides an update is needed.
Kevin Montrose
A: 

I don't see any benifit really. Let's say the average app has 3 primary goals:

  1. Rendering
  2. User input / event handlers
  3. Number crunching / Network / Disk / Etc

Dividing these into one thread each(several for #3) would be pretty logical and I would call #1 and #2 UI.

You could say that #1 is already multithreaded and divided on tons of shader-processors on the GPU. I don't know if adding more threads on the CPU would help really. (at least if you are using standard shaders, IIRC some software ray tracers and other CGI renderers use several threads - but i would put such applications under #3)

The user input metods, #2, should only be really really short, and invoke stuff from #3 if more time is needed, that adding more threads here wouldn't be of any use.