I am presenting at Wintellect’s Devscovery conference this week. At this conference, the attendees are able to set up 1-on-1 sessions with any of the speakers. One attendees wanted to ask me some questions about my Power Threading’s AsyncEnumerator class. They were using this class in a Windows Form project of theirs and I thought it would be best if I discussed what is going on in this forum.
In .NET 2.0, the CLR team added a System.Threading.SynchronizationContext class. This class provides a way for an application model (like Windows Forms, WPF, or ASP.NET) to describe its threading model (such as how to marshal work to a Windows Forms/WPF GUI thread). My AsyncEnumerator class takes advantage of this automatically so that iterator code executes on the GUI thread for Windows Forms and WPF apps. For ASP.NET, this allows my AsyncEnumerator to associate the client’s culture and IPrincipal information to thread pool thread that will eventually call into your iterator code.
When you construct an AsyncEnumerator, its constructor grabs a reference to the SynchronizationConext-derived object associated with the calling thread and saves this reference in a private field. Then, whenever my AsyncEnumerator calls into your iterator, it always does this via the SynchronizationContext-derived object. For Windows Forms/WPF, this means that your iterator gets invoked by queuing a request on your GUI thread’s message queue. This means, that your GUI thread MUST pump messages in order for your iterator to execute its code. The Devscovery attendee was calling AsyncEnumerator’s Execute method (instead of the preferred BeginExecute method) from their application’s GUI thread. The Execute method blocks until the iterator completes running and since this method was called form the GUI thread, the iterator was unable to run and so a deadlock occurred.
This behavior is by-design as the whole purpose of the AsyncEnumerator is to execute code asynchronously and, in a GUI application, to keep the UI responsive. In fact, I always discourage calling Execute at all. The only reason why the AsyncEnumerator even offers the Execute method is for testing or to demonstrate functionality.
However, I’d also like to point out that the AsyncEnumerator offers a SyncContext property and so, the Windows Forms/WPF developer can construct an AsyncEnumerator and then set its SyncContext property to null before calling Execute. This will allow thread pool threads (as opposed to the GUI thread) to call into your iterator – just make sure the code in your iterator doesn’t try to update any GUI controls.
Finally, I’d like to point out that an iterator can change its AsyncEnumerator’s SyncContext property within the iterator itself. This can be useful if you have some code in the iterator that can be run on any kind of thread and some code that must be run on the GUI thread (to update UI controls). In fact, in order to support this better, I’ve added a feature to the August 20th, 2008 version of the library. With this new version, within an iterator, you can change the SyncContext property and execute a “yield return 0”. When you do this, I will internally call ThreadPool.QueueUserWorkItem to have a thread pool thread continue your iterator. However, the thread pool thread will execute your iterator via the current SyncContext property. Another user wanted this feature so that they could improve the performance of their iterator code where some code needs to update the GUI and some other code needs to just do some compute-bound work (which doesn’t have to be on the GUI thread).