Practical .NET

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
  MessageQueue.Create(".\private$\orders", False)
End If
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:

q.BeginReceive()

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.

Reading Messages
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:

q.BeginReceive()

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.

About the Author

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/.

comments powered by Disqus

Featured

  • Hands On: New VS Code Insiders Build Creates Web Page from Image in Seconds

    New Vision support with GitHub Copilot in the latest Visual Studio Code Insiders build takes a user-supplied mockup image and creates a web page from it in seconds, handling all the HTML and CSS.

  • Naive Bayes Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the naive Bayes regression technique, where the goal is to predict a single numeric value. Compared to other machine learning regression techniques, naive Bayes regression is usually less accurate, but is simple, easy to implement and customize, works on both large and small datasets, is highly interpretable, and doesn't require tuning any hyperparameters.

  • VS Code Copilot Previews New GPT-4o AI Code Completion Model

    The 4o upgrade includes additional training on more than 275,000 high-quality public repositories in over 30 popular programming languages, said Microsoft-owned GitHub, which created the original "AI pair programmer" years ago.

  • Microsoft's Rust Embrace Continues with Azure SDK Beta

    "Rust's strong type system and ownership model help prevent common programming errors such as null pointer dereferencing and buffer overflows, leading to more secure and stable code."

  • Xcode IDE from Microsoft Archrival Apple Gets Copilot AI

    Just after expanding the reach of its Copilot AI coding assistant to the open-source Eclipse IDE, Microsoft showcased how it's going even further, providing details about a preview version for the Xcode IDE from archrival Apple.

Subscribe on YouTube

Upcoming Training Events