Smarter Multithreading: AJAX in Windows Forms and Software Transactional Memory
Whether juggling CPU- and IO-bound functions on the server, or doing heavy lifting on a client without blocking the UI thread, handing jobs off to thread pools is a fact of life for today’s programmers. In this post I examine the basic plumbing we’re using to get the job done, then compare it to the programming patterns AJAX is making all the cool kids learn.
.NET supplies its own very easy to use ThreadPool, but that class suffers from a few drawbacks. We’ve started to use Ami Bar’s Smart Thread Pool for our programming and are very pleased with it. Of most interest to us, it makes capturing thrown Exceptions simple and uniform. The Smart Thread Pool sits at the heart of our smart client UI. This UI does some very heavy lifting: corresponding with web services, uploading/downloading large files, and speaking with recalcitrant DCOM servers. We wanted a clean way for people to write code to handle these problems without blocking the UI thread. We settled on an explicit Controller/View pattern.
When our main form starts up, it creates a controller object. (Some names have been changed to protect the innocent).
public ThingForm()
{
...
_controller = new ThingController(this);
}
The controller handles all UI events asynchronously. For instance a button click does something like
_controller.AsyncInvoke(new WorkItemCallback(_controller.ScanForMxdSessions));
The AsyncInvoke function on the controller uses the Smart Thread Pool to spin up the task on another thread. This leaves the UI thread free to continue updating and responding to events.
ClientThreadPool.Pool.QueueWorkItem(
function,
this,
new PostExecuteWorkItemCallback
(HandleThreadPoolException));
This is where the smart thread pool comes in handy — I have a single function which can handle unexpected exceptions for all controller operations. This can do logging, put up a message box, etc.
This exception handler and other controller functions do need to eventually update the form UI. They use a sister function which invokes a delegate on the form’s STA thread.
StaInvoke(new StaThunk(Sta_UpdateComponentTree));
StaInvoke simply calls the parent Form’s Invoke function, which queues the delegate up to run on the parent STA thread. Then the Sta_XXX functions are free to pretend they’re single threaded (because they are being executed on the STA thread) and poke at the UI components all they want (in the case above, binding a tree view). We know they’ll execute quickly because they are only examining model state and making trivial updates to UI components.
This paradigm enables an extremely responsive user interface at the same time it allows execution of long-running queries. Sound familiar? It’s the AJAX pattern all the cool kids are learning, but in Windows Forms. In AJAX, tasks are always kicked off asynchronously, whether explicitly (e.g. XmlHttpRequest request or setTimeout), or implicitly (event handlers). The UI writer must present an interface that can continuously be updated by background worker tasks. Yahoo Maps beta is one of my favorite current examples of this.
JavaScript provides a lot of training wheels: all threading is hidden from you. But in .NET, you have to deal with it yourself. The pattern raises some interesting thread safety issues. If the user kicks off two instances of the same job, how can we be sure the data is presented correctly? In my example above, I am scanning for “MxdSessions”, and then updating a tree control with the results. Someone could press the button twice, and it’s utterly unknown who will get to the tree control first. Even worse, how do we keep from creating corrupted results, e.g. a list of MxdSession half from one request, and half from another?
We’re still looking at alternatives, but the keep-it-simple approach is working. Since the real work done on the “model” is multithreaded and doesn’t have to worry about blocking UI updates, we simply use explicit mutexes around certain controller functions. For instance, all functions that would be sensitive to “MxdSessions”, have a lock(_syncMxdScan) around the important bits. Sure, the user can press the button three times, but the lock will force those scans to happen serially. On the other hand, a user request to do something unrelated (like logging into a remote server), doesn’t need to block on any of this.
Then we get back to those UI-updating functions. They are freed from the need to synchronize around form data structures, because they are executing on an STA thread. However they still need to worry about the reliability of the data they’re reading from the controller — it could change while they’re looking at it. Using the same mutex that the controller functions use, they attempt to pull a consistent chunk of data out of the controller.
NameAndId[] orgs = null;
Study[] studies = null;
lock(_syncContextUpdate)
{ orgs = _ownerOrgs; studies = _studies;}
This function can now use orgs and studies at will, knowing they are working with a copy of the data that will remain consistent during their execution, because all controller functions which could change them also lock on _syncContextUpdate.
It makes one yearn to simply treat the model’s in-memory state as a database. A modern RDBMS automatically takes care of ensuring you have a consistent view of data during your own transaction. I’m convinced that Software Transactional Memory libraries will go mainstream very soon, enabling this much easier programming model.
In the meantime, the above ramblings lead me to be a bit more dubious of JavaScript’s training wheels. If I am updating model state at the same time someone else is rendering it, how can I be sure of consistency? Despite a great deal of searching, I still don’t know how many threads the JavaScript interpreters in major browsers are using. And even if I did, there’s no such thing as a mutex in JavaScript. In another post I’ll examine how Software Transactional Memory might be simulated in JavaScript and .NET.
Technorati Tags: programming
