Archive for September, 2009
Something that has always bugged me about the .NET Base Class Library is the absence of a threading lock that would reliably tell me whether another thread was already doing something, and if so, not to block and wait for it, but just to continue on as if nothing has happened.
This becomes super useful in many multi-threaded situations, one of the main ones being User Interface threading where the foreground thread is requesting some data via an asynchronous call. Typically in this scenario, if the user makes a selection from the UI, you want the request to be sent, but if a request is already running, to ignore the user’s selection until you’re done getting the previous requests data, then at the end you can check if there was a contention and re-execute if necessary. This results in the UI remaining responsive (as in there are no locks to wait for on the foreground thread) but still ensuring the application stays in sync with the users requests.
The example I’ll give is a Windows Forms application (although it could just as easily be a WPF or Silverlight application) that searches the web and displays the html result in a textbox. I want this to be super responsive so I’ve made the application search as the user types.
Now a simple way of approximating the threading behaviour I’m after is just checking a boolean and then setting it, like so:
The problem with this code is that it can’t tell me whether the user changed their text and clicked again while the WebClient was still performing the first search and it isn’t particularly thread safe if I wanted to fire off the search from a background thread.
What’s wrong with Monitor.TryEnter?
There are a few things that have bugged me about Monitor.TryEnter, the main one being that the thread that calls Exit must be the thread that originally called TryEnter. This is all well and good in some scenarios, but in the above example it would mean I would have to call SearchWebAsync always from the UI thread. The other obvious point about Monitor.TryEnter is it doesn’t track how many times TryEnter has been attempted for a particular lock object. Not to mention it uses locking and not Interlocked statements (which are faster so I’m told although I’ve yet to see any benchmarks which say just how much faster).
Enter the ExclusiveSection class (or should I say TryEnter?)
An ExclusiveSection is used similarly to a lock in that you create an instance and then attempt to enter that section, when you do so, the ExclusiveSection.TryEnter method will return whether you were successful or not, if not you can then simply exit, knowing that the ExclusiveSection has remembered that you asked to do so.
The Final Result
Now I can change my code so that it uses an Exclusive Section to ensure the code only ever performs a single query at a time, and but the most recent text query entered by the user is always respected.
Interestingly the call to SearchWebAsync can now be made from any thread, and will remain thread safe, (as long as the WebClient_DownloadStringCompleted event performed an Invoke on the UI thread to access the Windows Forms controls). This could be useful if you wanted to add a timer thread that fires off a call to refresh the search results every 30 seconds or so.
This kind of threading class is extremely useful in Silverlight where only asynchronous calls to get data are allowed.
Hope you find the ExclusiveSection class as useful as I do!