Asynchronous MSMQ Processing
Peter wraps up his look at offloading processing from the mainline of your application in order to improve response time by looking at processing message queues asynchronously.
In previous columns, I looked at setting up and writing to a Microsoft Message Queue and processing the messages in a Message Queue. In that last column, I used plain-old synchronous programming to read messages from the queue using the MessageQueue Receive method. That strategy has problems because the Receive method blocks execution until a message shows up on the queue. That's not necessarily a bad thing: In a Windows Service, you probably want your code to wait for a message and process the message as soon as it appears.
In many cases, however, that blocking action is not what you want. Sometimes you just want to check to see if a message is present, and if there is no message, go on to something else. Even in the examples I used in my previous columns, terminating processing was awkward because you had to set a time limit for the Receive method and catch the error that resulted when the Receive method "timed out." The code wasn't awful (and does work), but it's inelegant.
Using asynchronous processing with the Receive method involves about the same amount of code as synchronous processing, but is more appopriate when your application has something else to do than wait around for a message to show (for example, checking to see if you should terminate).
Setting Up the Queue
The first step in your asynchronous code is to set up your queue. The variable that holds your reference to the queue must be declared globally, because you'll be accessing the queue from multiple methods. If you're using Visual Basic, you'll need to declare that variable with the WithEvents keyword, like this:
Dim WithEvents q As MessageQueue
At some point early in the life of your application, you'll need to check to make sure the queue exists, create the queue if it doesn't and, finally, set your variable to a reference to the queue. That's what this code does:
If Not MessageQueue.Exists(".\private$\orders") Then
q = New MessageQueue(".\private$\orders")
Now you're ready to begin processing your queue asynchronously. The easiest way is to call the queue's BeginReceive method:
Calling BeginReceive causes your application to wait for a message to appear on the queue -- but that wait is now processed on a separate thread. Your application will proceed to whatever line follows the call to BeginReceive.
As messages show up on the queue, the queue fires a ReceiveCompleted event. If there are already messages on the queue, the event fires immediately after you call the BeginReceive method. To process a message asynchronously, you just need to tie some code to that event.
In Visual Basic, to tie an event handler method of your own to the event you can use the Handles keyword on the method's declaration. That method must accept the standard parameters for an event with the e parameter declared as ReceiveCompletedEventArgs:
Public Sub ReadQueue(sender As Object, e As ReceiveCompletedEventArgs) Handles q.ReceiveCompleted
In C# you tie an event handler to the event with code like this (you should put this code before the call to BeginReceive):
q.ReceiveCompleted += ReceiveCompletedEventHandler(ReadQueue);
Similarly, you can add an event handler dynamically in Visual Basic with the AddHandler keyword, and omit the Handles keyword on the method:
AddHandler q.ReceiveCompleted, AddressOf ReadQueueEvent
Within your method you use your queue variable's EndReceive method to process the message, passing the e parameter's AsyncResult property. This code retrieves the message that triggered the event, and assumes the body of the message contains an Order object formatted using the .NET XmlMessageFormatter class:
Dim msg As Message
Dim ord As Order
msg = q.EndReceive(e.AsyncResult)
Dim msgType(0) As Type
msgType(0) = GetType(Order)
msg.Formatter = New XmlMessageFormatter(msgType)
ord = msg.Body
Calling EndReceive terminates the BeginReceive call. So, after you've done whatever processing you wanted to do with the message, you should set up to process the "next" message (which may, of course, have already arrived and be sitting on the queue). That's easy: Just call the BeginReceive method at the end of your event handler:
If all you want to do is some foreground processing while the BeginReceive method checks on your queue in the background, you can also call the EndReceive method outside of an event handler method. However, if there's no message on the queue, the EndReceive method will hang until a message appears on the queue.
This column finishes my discussion of using the MSMQ to offload processing from your application's mainline. It's an option you should explore whenever you're looking for a way to be more responsive to your users while at the same time handling long-running processes.
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.