Asynchronous Processing

The code we have used in the examples so far in this chapter performs all requests synchronously, which means that the calling thread blocks while the object manager services the request - which can on occasions take a long time. It is also possible to ask for the request to be performed asynchronously on a separate thread - and, unusually for .NET, the mechanism is not based on the BeginXXX()/EndXXX() design pattern that we discussed in Chapter 9. Instead, to request asynchronous processing, you instantiate an object of type ManagementOperationObserver(). Taking the ManagementClass.GetInstances() method, the code uses a one-parameter overload of GetInstances(), and looks like this:

 ManagementClass modemClass = new                    ManagementClass(@"\\.\root\CIMV2\Win32_POTSModem) ; ManagementOperationObserver observer = new MamgementOperationObserver(); // Initialize observer by adding handlers to events. modemClass.GetInstances(observer); 

This works as follows: the ManagementOperationObserver class defines a number of events, and before calling the one-parameter overload of GetInstances(), you should add appropriate handlers to the events that you are interested in. This overload of GetInstances() returns immediately, and the WMI object manager will raise the events as appropriate. The events will be handled on managed thread-pool threads, not on the main application thread.

The events available are:

Event

Meaning

Completed

Indicates that the operation is completed.

ObjectReady

This event is raised whenever a new object becomes available as a result of a query.

ObjectPut

A Put() operation has been successfully completed.

Progress

This event is raised at intervals to indicate the progress of the operation.

It should be clear now why Microsoft has chosen not to use the BeginXXX()/EndXXX() architecture here: one operation can give rise to a succession of events. For example, a GetInstances() query will cause the ObjectReady() event to be raised each time a new object is returned from the WMI provider. The BeginXXX()/EndXXX() architecture has not been designed for this kind of scenario.

You will notice that the list of events includes an ObjectPut() event, which is clearly only appropriate when writing values to an object - it's not appropriate for a SELECT query. The reason this event is here is that the ManagementOperationObserver-based design pattern is not only used for queries: it's used for quite a few different methods that call up the object manager, including ManagementClass.GetInstances(), ManagementObjectSearcher.Get(), ManagementObject.Get(), and ManagementObject.Put(). For example:

 ManagementOperationObserver observer = new ManagementOperationObserver(); // Initialize observer by adding handlers to events // Assume myModem and myModem2 are references to objects that describe modems. myModem.Put(observer); // Asynchronously writes data to modem myModem2.Put();        // Do same thing but synchronously 

As far as the various events defined by ManagementOperationObserver are concerned, Microsoft has defined suitable event handler delegates for each of them; for example, the Completed event is of type CompletedEventHandler, which has this definition:

 public delegate void _CompletedEventHandler(object sender,                                             CompletedEventArgs e); 

CompletedEventArgs contains information appropriate to this event (in this case, the status - whether the operation completed successfully, or whether it failed and if so, why). There are similar handler and event args definitions for the other events in ManagementOperationObserver. The best way to see this, however, is with an example. In the next section we develop the earlier ListProcessors example so that it retrieves its results asynchronously.

ListProcessorsAsync Example

This example retrieves the list of processors on the computer, just like the earlier similar examples, but in this case we retrieve the list asynchronously. This example will demonstrate the ObjectReady and Completed events.

This is the code for the Main() method:

 static void Main() {    ManagementObjectSearcher processorSearcher = new ManagementObjectSearcher(         "SELECT Name, CurrentClockSpeed FROM Win32_Processor");    ManagementOperationObserver observer = new ManagementOperationObserver();    CallBackClass callBackObject = new CallBackClass();    observer.Completed += new                       CompletedEventHandler(callBackObject.OnAllProcessors);    observer.ObjectReady += new                     ObjectReadyEventHandler(callBackObject.OnNextProcessor);    processorSearcher.Get(observer);    Console.WriteLine("Retrieving processors. Hit any key to terminate");    Console.ReadLine(); } 

In this code, we set up the ManagementObjectSearcher instance that will make the query request. Then we set up the ManagementOperationObserver callback object, and supply handlers to its ObjectReady and Completed events. The handlers we supply are defined in a class, CallBackClass, which we'll define shortly. Having done all the preparatory work, we call ManagementObjectSearcher.Get() to perform the query. This call returns immediately, and in a real application, the main thread would probably go off and do some other work now. However, since this is only an example, I've coded it so the main thread simply waits for some user input to terminate the process.

Here's the definition of CallBackClass, which implements the event handlers. Remember that these handlers will be executed on a thread-pool thread, not on the main thread.

 class CallBackClass {    int totalProcessors = 0;    public void OnNextProcessor(object sender, ObjectReadyEventArgs e)    {       ManagementObject processor = (ManagementObject)e.NewObject;       Console.WriteLine("Next processor object arrived:");       Console.WriteLine("\t{0}, {1} MHz", processor["Name"],                                              processor["CurrentClockSpeed"]);       ++totalProcessors;    }    public void OnAllProcessors(object sender, CompletedEventArgs e)    {       if (totalProcessors > 1)          Console.WriteLine("\n{0} processors", totalProcessors);       else          Console.WriteLine("\n{0} processor", totalProcessors);    } } 

As we can see, OnNextProcessor() (which we set up as the handler that is called each time a new result is ready) displays details of this processor, and increments a count of how many processors there are. OnAllProcessors() (the handler for the Completed event) simply displays the number of processors returned. In order to keep this example as simple as possible, I've taken a couple of shortcuts that you probably wouldn't do in a real application. In particular, you'd probably not want worker threads handling user output, and the OnAllProcessors() method in particular would be more likely to do something to signal to the main thread that the process is complete.

Running this example on my machine gives this result:

 Retrieving processors. Hit any key to terminate Next processor object arrived:      AMD Athlon(tm) processor, 1199 MHz 1 processor 



Advanced  .NET Programming
Advanced .NET Programming
ISBN: 1861006292
EAN: 2147483647
Year: 2002
Pages: 124

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net