views:

565

answers:

6

http://weblogs.java.net/blog/kgh/archive/2004/10/multithreaded_t.html argues that multithreaded GUI frameworks are a failed dream. What about non-GUI frameworks? Does this rule of thumb extend to all event-driven frameworks?

Here is a quote from the article that caught my attention:

The problem of input event processing is that it tends to run in the opposite direction to most GUI activity. In general, GUI operations start at the top of a stack of library abstractions and go "down". I am operating on an abstract idea in my application that is expressed by some GUI objects, so I start off in my application and call into high-level GUI abstractions, that call into lower level GUI abstractions, that call into the ugly guts of the toolkit, and thence into the OS. In contrast, input events start of at the OS layer and are progressively dispatched "up" the abstraction layers, until they arrive in my application code.

Now, since we are using abstractions, we will naturally be doing locking separately within each abstraction. And unfortunately we have the classic lock ordering nightmare: we have two different kinds of activities going on that want to acquire locks in opposite orders. So deadlock is almost inevitable.

+1  A: 

No. Even as a rule of thumb, it's simply saying "it's hard to make them work." But event-driven frameworks are very much like event-driven simulation and various other things; the fact that it's hard in Javaworld isn't a fact about multithreading, but about the abstractions available in Java.

In another environment, like Erlang, it's both fairly natural, are pretty robust.

Update

It still sounds like he has the wrong abstractions. I don't see anything inherent in the problem that forces a locking issue. The key, I think, come in here:

Now, since we are using abstractions, we will naturally be doing locking separately within each abstraction. And unfortunately we have the classic lock ordering nightmare: we have two different kinds of activities going on that want to acquire locks in opposite orders. So deadlock is almost inevitable.

So why is deadlock almost inevitable? Because two different kinds of activities are going on that want to acquire locks in opposite orders. And that is because "we will naturally be doing locking separately within each abstractions."

In other words, he's chosen -- or had chosen for him by the environment -- abstractions that don't support his needs. It follows, he appears to claim, that thus there are no abstractions that would. I don't find this convincing. I'd start by examining two things:

  • "naturally we will be doing locking separately within abstractions", and
  • "we have events going on that want to acquire locks in opposite orders."

In my experience, "naturally X" usually means "I haven't really looked for other options." And I very much doubt that the events want to acquire locks in opposite orders; yu get a lock, you do something, and you release it.

The point is, he appears to be presenting the fact that he finds it hard to come up with a scheme that does work as a theorem to say that no scheme can work.

Without a lot more details about the problem, it's hard to construct a counter-example, but as I said above, there are lots of examples of systems that have events going on every which way, asynchronously, from internal processes and GUIs, that manage to have many concurrent threads of control and don't deadlock. Erlang came to mind because one class of those problems, telephony, is the one for which Erlang was invented, and in fact those sorts of issues are why Erlang was invented.

Charlie Martin
You're making a mistake equating Erlang processes and threads. The Erlang people don't do this, and there are some pretty fundamental differences (e.g. all the ones that make Erlang nice for parallel processing!).
Mark Harrison
@Mark In response to comment: your understanding of the question is, I believe, flawed. While clearly an Erlang process is distinct from a Java thread, "multithreaded" in this context is about event-driven frameworks with multiple concurrent threads of control, ...
Charlie Martin
...not the implementation specifics of how they're generated. Erlang is an excellent example of how a better, more expressive set of primitives can make concurrent programming easier and more reliable.
Charlie Martin
Charlie, please comment on the deadlock quote I just added to the question,
Gili
This is a very vague answer. Why doesn't Erlang have these problems then?
Wim Coenen
Because Erlang is a functional language that is fully concurrent and (since it doesn't have mutable variables) incapable of having the same kind of deadlocks. But then, what you really mean is "I don't know enough about Erlang to understand." So see http://erlang.org
Charlie Martin
@Charlie: I know that. I was just pointing out that you don't mention this information in your answer.
Wim Coenen
Man, there's only so much you can write in one answer.
Charlie Martin
I was thinking the same thing as wcoenen. Charlie, you shouldn't assume that readers know Erlang (or should have to learn it before understanding your answer). Just my 2 cents.
Gili
"In other words, he's chosen [..] abstractions that don't support his needs." He is completely right, for object-oriented abstractions. The OS class shouldn't be aware of all possible UI widget classes and vice-versa. What you said certainly makes sense for functional languages but not for OO langs.
Gili
@Gili, there's still only so much you can write in one answer. On the other point, what you're saying is isomorphic to "this particularly abstraction isn't working out in Java, using the particular setup I want, so all multithreaded event-driven frameworks are intractably hard."
Charlie Martin
Like all "for all" expressions, the existence of a counter-example falsifies the assertion. It just isn't true that all event-driven frameworks should be single-threaded; it is true that they're harder than multi-threaded, especially if the structure of a framework doesn't suit.
Charlie Martin
+2  A: 

I'm wondering how much of the problem comes from the fact that multithreading is not appropriate for event-handling, and how much it is that many of the developers who build GUIs are not proficient with multithreading.

I tend to side with the later case, and also with chunkiness of some of the frameworks. For example, I find GUI code in Eclipse to be fairly annoying to write because there isn't a more convenient way to deal with heavy logic and GUI code.

Uri
+1  A: 

In general, yes, since event driven frameworks are similar to the Reactor pattern, which stipulates a main loop which waits for events (the "event loop", and then invokes the registered callbacks for the various elements.

This is how event-driven frameworks have been traditionally defined. Here's a nice description about how Erlang processes are tied to the VM's event loop:

Erlang processes are tightly integrated with the Erlang VM’s event-driven network IO core. Processes can “own” sockets and send and receive messages to/from sockets. This provides the elegance of concurrency-oriented programming plus the scalability of event-driven IO (the Erlang VM uses epoll/kqueue under the covers).

http://yarivsblog.com/articles/2008/05/18/erlang-vs-scala/

update: here's some interesting notes that were very influential on me at the time. Note in particular how locks and callbacks interact badly.

http://home.pacbell.net/ouster/threads.pdf

Mark Harrison
The reactor pattern is intended for a particular situation, namely marshalling/unmarshalling objects to/from the network. It is generally agreed the best performance with today's OS's is obtained by doing network IO in a single thread. There is no *general* reason ...
ceretullis
... The rest of an event driven app needs to be single threaded.
ceretullis
I'm not sure of a general reason, but it does seem to be the case that most projects, when they try to integrate threads, experience problems of one kind or another and usually fall back to removing the threads. Just my observation!
Mark Harrison
+3  A: 

I would have to say no, with a caveat. Event driven frameworks that revolve around shared state, such as a UI, should be single threaded. Event driven frameworks that revolve around notifications, such mechanical monitoring(e.g. letting you know when the pressure in a pipe is too high), could be single threaded, but might be more appropriate to be multi threaded.

It is certainly possible to build a multi threaded UI framework, and I have done so myself. In the end I converted it to be single threaded. Part of the reason does fall under what Charlie said about "it's too hard". The problem was that with a multi-threaded UI framework, it wasn't just me that had to deal with the threading, but anyone that used the UI. The core was certainly thread safe, but then anyone that wrote a control had to make that thread safe as well. Nevermind that when making multiple changes to the UI, you had to notify the core that you were doing so so you didn't get partial updates. Since a user is generally a pretty slow thing, any performance gain was really negligible, and could be worked around with an asynchronous call if necessary for specific cases. Single threading is actually the appropriate model here.

On the other hand, in a model where there isn't(or isn't much) shared state, a multi threaded model makes eminent sense. There's no reason for one event(the reactor is on fire) to be delayed for the 30 seconds it takes for your query(Bob the operator just clocked out) to timeout because some yahoo in operations left the punch_card table locked.

Darren Clark
I don't understand your definition of "shared state". Why would you say that UI frameworks have shared state and other frameworks do not? I don't see what is "shared" about UI frameworks. Are you referring to the fact that UI components tend to interact with one another?
Gili
I'm referring to the actual window itself mainly. In a UI when a user acts, you usually do some activity(which is probably independent, and could be threaded if it's long running), then update the UI. It's the drawing surface of the UI that is in the end shared.
Darren Clark
Darren, I see your point, but X doesn't require that.
Charlie Martin
A: 

Yes and No.

Event driven frameworks usually allow you to do things that usually require multiple threads with 1 thread. That's why they are usually single threaded.

However, it is not in any definition that an event driven framework cannot dispatch events to multiple threads.

Unknown
+1  A: 

This rule of thumb doesn't apply at all to non-GUI frameworks. See Welsh's SEDA architecture, see yield (code.google.com/p/yield) for a good python/c++ hybrid implemenation.

ceretullis
Good point! What you say makes sense for app servers because each connection is completely independent of another. I guess what he wrote applies for any framework where event generators are interrelated in some way.
Gili