Simplifying the Use of the APM in Windows Forms

This article is largely referenced from the Microsoft MSDN library and assumes a working knowledge of the Microsoft .NET Framework, threading, and the .NET Asynchronous Programming Model. MSDN, amongst others, insist that the future of application programming involves dividing your application into multithreaded parts, because of the growth of the microprocessor industry. Moreover, MSDN .NET developers insist that the future of multithreading involves dividing your application into callback sections. If this article appears too referenced without original content, then please let me know what constructive advice I should take. If you are creating a Microsoft Windows Forms application, and have an object with methods that may take some time to execute, you may want to consider writing an asynchronous API. Say, for instance, you have an object that downloads large files from a remote location. Without an asynchronous API, a client's UI would freeze for the duration of the call. With an asynchronous UI, the client's UI would not freeze. You could even construct the asynchronous API in such as way as to give progress updates to the caller, and give the client the opportunity to cancel the call. Few situations are as frustrating as a frozen UI that can only be cancelled by resorting to Task Manager to kill the process.

That said, several issues need to be made viable if you want to call objects asynchronously and build asynchronous APIs for your own business objects. This article will focus on these issues by walking through building an asynchronous API for a simple business object. We will also walk through the use and implementation of helper classes which simplify the task of implementing an asynchronous API. These helper classes are included in the sample code; they should be helpful in writing your own asynchronous APIs. Try to keep an open mind as we analyze these issues; they will appear complex at first, but are actually simple. If, in my limited knowledge, I can gain a grasp on them, then they are not too complex for the normal developer.

A Simple Business Object

Our business object has two methods: Method and GetNextChunk. Method takes a string and returns a string, and includes a 4-second call to sleep to simulate a long call. GetNextChunk simulates getting data from a query in pieces, and also has a built-in delay.

Here is the catch: the problem with having a Microsoft Windows Forms client call these APIs is that they will freeze the UI for significant periods of time. We will have to implement an asynchronous API for the Windows Forms client code writer to use. In this article, we will focus on just that.

Asynchronous API

If we want to implement an asynchronous API for our class, it makes sense to follow the pattern used in the .NET Framework for asynchronous APIs. Moreover, it makes even better sense to follow some of Wintellect’s Power Threading library examples. The .NET Framework makes a method such as GetResponse asynchronous by implementing a BeginGetResponse method and an EndGetResponse method. Take, for example, this excerpt from System.Net.WebRequest:

Following this pattern, we will implement the following four methods: BeginMethod, EndMethod, BeginGetNextChunk, and EndGetNextChunk. In order for these methods to return immediately to the caller, we cannot call Method or GetNextChunk on the thread executing inside these asynchronous APIs. Instead, we will need to queue the calls to the synchronous method, and the client callback to another thread. We will leverage the System.Threading.ThreadPool class in the .NET Framework to do this. Furthermore, in order to simplify the implementation of BeginGetNextChunk and EndGetNextChunk, we will leverage several helper classes. Let's first take a look at the business objects asynchronous API, which is implemented with these helper classes. Then, we can jump into the details of these classes to see how they work.

With a delegate to our synchronous method, we can use Asynchronizer.BeginInvoke and Asynchronizer.EndInvoke to implement our methods. For instance, our sample business object has the method GetNextChunk, which takes an integer as a parameter and returns a customer array. So we declare the delegate:

protecteddelegate Customer[] GetNextChunkEventHandler(int chunksize);

and pass an instance of it to Asynchronizer.BeginInvoke. So now, it’s time to take a more detailed look at the helper classes that enabled the simple solution above:

AsyncUIHelper Classes

Table 1

AsynchronizerResult Class

Description

public object AsyncState

Gets a user-defined object that qualifies or contains information about an asynchronous operation.

public WaitHandle AsyncWaitHandle

Gets a <link tabindex="0" keywords= "frlrfSystemThreading WaitHandleClassTopic" /> WaitHandle </link /> that is used to wait for an asynchronous operation to complete.

public bool CompletedSynchronously

Gets an indication of whether the asynchronous operation completed synchronously.

public bool IsCompleted

Gets an indication of whether the asynchronous operation completed synchronously.

A constructor that initializes AsynchronizerResult with a delegate to the synchronous method (method), the delegate to call back the client (callBack), the client state to pass back to the client (asyncState), a placeholder for the Asynchronizer object that created AsynchronizerResult, and a Control to call Control.Invoke on (ctr).

public void DoInvoke(Delegate method, object[] args)

Calls the delegate to the synchronous method through Delegate.DynamicInvoke.

private void CallBackCaller()

Calls the client callback delegate that was passed to the constructor.

A constructor that initializes the Asynchronizer with the Control to call Control.Invoke on (control), the delegate to call back the client (callBack), and the client state to pass back to the client (asyncState).

Inspects the delegate's Target property, and if it is a subclass of Control, calls the delegate through Control.Invoke.

How These Two Work Together

The constructor for Asynchronizer saves the client callback function that will be called when the synchronous method completes. It also saves the state the client wants maintained, which is passed back to the client during callback.

Asynchronizer.BeginInvoke uses the help of AsychronizerResult to queue up calls to the synchronous method, and sets the client callback delegate to execute when this method completes. The call to Asynchronizer.DoInvoke is queued with the help of the .NET Framework class ThreadPool.QueueUserWorkItem.

Asychronizer.EndInvoke gets a value from the synchronous method call by inspecting AsynchronizerResult.MethodReturnedValue. The return value from this method is the return value from the call to the synchronous method.

Using These Helper Classes

As you will recall, the business object programmer who wants to implement an asynchronous API needs to implement a BeginMethod and EndMethod for any method he wants to expose asynchronously. This can be achieved by delegating the work to the helper methods Asynchronizer.BeginInvoke and Asychronizer.EndInvoke. Here again is the sample we saw above implementing BeginGetNextChunk and EndGetNextChunk. You should now understand how Asynchronizer and AsynchronizerResult work together to enable this straightforward implementation:

OK. So far so good. But there is an inevitable problem lurking here. The callback from the thread pool to the Windows Forms client occurs on a thread-pool thread, and not on the Windows Forms thread. This is not the supported way of executing callbacks to a Windows Form. In fact, the only methods of a control that can be called on a different thread are Invoke, BeginInvoke, EndInvoke, and CreateGraphics. Here, the developer will need to know to only call other methods of Control through Control.Invoke inside of the callback handlers. Below is an example implementation of a safe callback from a client's call to BeginGetNextChunk. The callback ChunkReceivedCallbackSafe immediately uses Control.Invoke to execute any code that updates the UI.

Making Business Object Callbacks Safe to Call from Controls

It is possible for a class that implements an asynchronous API to do more of the work, simplifying the work of the client programmer. In this article, we'll explore two approaches to accomplish this:

Including Control as a parameter to the asynchronous API. Any callbacks to Control go through Control.Invoke.

Investigating Delegate.Target, which holds the object that has the callback. If this is a Control, callback occurs through Control.Invoke.

Pass Control to Business Object

These additions to Asynchronizer allow Control to be passed in so that callback can be through this control's Invoke method. We add a parameter to the constructor of Asynchronizer that takes a control as input. This will be the control to call Control.Invoke on. We need to modify BeginGetNextChunk as well, so that this control is passed in. We do so by implementing BeginGetNextChunkOnUIThread, so that the first parameter is a control to execute callback on.

Use Delegate.Target to Test if Callback is on a Windows Forms Control

Unfortunately, using BeginGetNextChunkOnUIThread places a burden on the client programmer to remember to use this API, versus BeginGetNextChunk, which can be used by non-Windows Forms clients. But, there is a better way. We can take advantage of the fact that any delegate includes the Target property. This property holds the target object for the delegate. We can therefore inspect this property to determine whether or not a delegate callback is taking place in a Windows Form, by determining whether or not Target is a subclass of Control. Like so:

Using this method, we place no burden on the client to pass in the control the client is running on to the business object. So, we don't have to pass the Windows Forms this pointer into the call to BeginNextChunk.

Using Components to Simplify Client Callback Code

Implementing an asynchronous API requires client-side programmers to be familiar with the .NET Async Programming pattern. Programmers need to be familiar with the BeginMethod and EndMethod Model, and with the use of IAsyncResult. You can expose an alternative asynchronous API to your class with the help of events. For our sample business class, we can add the GetNextChunkCompleteEvent event to the class. This way, we can get rid of the requirement to pass a callback to the asynchronous method call. Instead, the client adds and removes handlers for this event. Here is this new API for the business object:

If the business object is a component, the client event handlers can be set through the UI. If the business object component has been dragged onto the design surface of the client, as in Figure 1, then you can select properties of this component to set the event handlers. On the client side, because we no longer call EndGetNextChunk to get the results of the method call, we use GetNextChunkEventArgs, which has the Customers property to pull off the array of customers (after dragging and dropping the business component onto the form).

Share

About the Author

I started electronics training at age 33. I began studying microprocessor technology in an RF communications oriented program. I am 43 years old now. I have studied C code, opcode (mainly x86 and AT+T) for around 3 years in order to learn how to recognize viral code and the use of procedural languages. I am currently learning C# and the other virtual runtime system languages. I guess I started with the egg rather than the chicken. My past work would indicate that my primary strength is in applied mathematics.

Comments and Discussions

Einstein used to say "Great spirits have always encountered violent oppostion from mediocre minds", truth is my grand father said that long before Einstein was born who's to credit? plus you have to add the fact that "Great minds discuss ideas; average minds discuss events; small minds discuss people" and you wonder why someone would spend so much time criticising content based on who or what published it first as opposed to discussing the actual value of the knowledge within the content? I guess that speaks for itself.
On the other end I know there are better undocumented techniques Microsoft has not done a good job discussing either from the lack of knowledge or intentionally. Your article inspires me a great deal about disclosing some of these fine techniques I have come to utilize and engineer over the years of my expertise Thanks Pal!

Not stolen, pal. It was submitted with a previous request that I seek help in referencing. That means that anything at all that is not mine originally must be referenced; I wrote to the web master about how to do that. This request went to the webmaster days before I tried to submit an article that focused on multithreading. My previous article about asynchronous delegates resulted in about 20 emails requesting clarification about control.BeginInvoke, a concept alien to me. This is why I posted this article as a contribution.

No, you really did steal it. You copied the original article nearly word for word, down to each and every code sample matching variable names. When you take entire bodies of text from another source and place them in your document verbatim without quoting or citing, that is plagiarism. Worse is that you simply tweaked text here and there so it looked like your own.

I mean, come on man, look:

MSDN article

Your article

...Following this pattern, we will implement the following 4 methods: BeginMethod, EndMethod, BeginGetNextChunk, and EndGetNextChunk. In order for these methods to return immediately to the caller, we cannot call Method or GetNextChunk on the thread executing inside these asynchronous APIs. Instead, we will need to queue the calls to the synchronous method, and the client callback to another thread. We will leverage the System.Threading.ThreadPool class in the .NET Framework to do this...

...Following this pattern, we will implement the following four methods: BeginMethod, EndMethod, BeginGetNextChunk, and EndGetNextChunk. In order for these methods to return immediately to the caller, we cannot call Method or GetNextChunk on the thread executing inside these asynchronous APIs. Instead, we will need to queue the calls to the synchronous method, and the client callback to another thread. We will leverage the System.Threading.ThreadPool class in the .NET Framework to do this...

...As you recall, the business object programmer who wants to implement an asynchronous API needs to implement a BeginMethod and EndMethod for any method he wants to expose asynchronously. This can be achieved by delegating the work to the helper methods Asynchronizer.BeginInvoke and Asychronizer.EndInvoke. Here again is the sample we saw above implementing BeginGetNextChunk and EndGetNextChunk. You should now understand how Asynchronizer and AsynchronizerResult work together to enable this straightforward implementation...

...As you will recall, the business object programmer who wants to implement an asynchronous API needs to implement a BeginMethod and EndMethod for any method he wants to expose asynchronously. This can be achieved by delegating the work to the helper methods Asynchronizer.BeginInvoke and Asychronizer.EndInvoke. Here again is the sample we saw above implementing BeginGetNextChunk and EndGetNextChunk. You should now understand how Asynchronizer and AsynchronizerResult work together to enable this straightforward implementation:...

...All is well and good, but there is an unexpected problem lurking here. The callback from the thread pool to the Windows Forms client occurs on a thread-pool thread, and not on the Windows Forms thread. This is not the supported way of executing callbacks to a Windows Form. In fact, the only methods of a control that can be called on a different thread are Invoke, BeginInvoke, EndInvoke, and CreateGraphics...

...OK. So far so good. But there is an inevitable problem lurking here. The callback from the thread pool to the Windows Forms client occurs on a thread-pool thread, and not on the Windows Forms thread. This is not the supported way of executing callbacks to a Windows Form. In fact, the only methods of a control that can be called on a different thread are Invoke, BeginInvoke, EndInvoke, and CreateGraphics...

The entire article, down to the structure and wording and code sample variable names, is plagiarised.

I liked the article, however i must admit that I am also sensitive when people copy from another source. Its fine to copy stuff to support an article or your thesis or whatever that is but you must acknowledge the original author. A simple link would do just fine. You would like me to copy some part of your work and effort and present it as my original work, would you?

I liked the article, but I want to point some traps of using AsyncCalls.

The documentation about Async calls is a bit misleading and it's uses are somewhat confusing.
The first point is: When would you use separate threads to do the work?
I think that, in general, in slow operations that would "freeze" the aplication. But, then, the ThreadPool is for fast operations... so, slow is somethink like "a few seconds", but not "a minute or more".

Why?
Because there is a finite number of threads (25 by default, I think).
In client programs this is not a problem, but in a server, you can kill the aplication when you need the 26th async call to run, if it waits for the other threads to finish and, for instance, those threads are really slow for external factors (like the network). Effectivelly, the CPU is not working as it is waiting for the network. The new thread can't start, as the ThreadPool expects to use only the 25 threads, as an way to avoid many threads to downgrade performance and, after all, the performance is being downgraded by unnecessary waits.

Not to point that, in some cases, an async call can call another async call but, at the end of the method, wait for it to finish. If we start 25 async calls that do this, they will create more async requests and wait for them to finish before continuing and they, for instance, wait for some thread to finish before they start. It is a perfect dead-lock.

As I said, this only affects servers, not clients, but in the company I work the system was freezing many times with "no apparent reason" for thinks like that.

Hi, and thanks for the feedback. For what it's worth, asynchronicity and its pursuit began when Win32 developers became used to accessing APIs synchronously. As you stated, a thread-synchronous operation (and in particular, and I/O operation) means that the entire thread waits until the I/O operation is complete; a thread initiated some task, and then waits patiently for the task to complete. If the code reaches a higher level of sophistication, it could create a worker thread to make the synchronous call, freeing the main thread to continue its work. Using worker threads to perform lengthy blocking calls is crucial for GUI applications because blocking the thread that pumps the message queue disables the user interface of the application. But when a thread is blocked, other threads are created that begin to consume resources. They may not be consuming CPU cycles, but they will degrade performance because of that resource consumption. And documentation insists that thread creation is costly. But more importantly, in terms of I/O, delays caused by track and sector seek time on random access devices (such as discs and DVDs), delays caused by the relatively slow data transfer rate between a physical device and memory, and delays in network data transfer using file servers, storage-area networks, et. al, all contribute to either a delay or a routine that requires too long a time without an asynchronous call to a method. In .NET, when a thread makes an asynchronous call to a method, the caller returns immediately. The caller thread is not blocked; it is free to perform some other task. The .NET infrastructure obtains a thread for the method invocation and delivers the parameters passed by the calling code.

A useful underlying concept involves the Stream class of the System.IO namespace. The Stream class enables asynchronous reads and writes by using the the APM pattern, which enables scalable I/O -- for example, I/O completion ports on Windows -- and permits threads to make forward progress in parallel with the stream operation. This can help if you are responding to an event on the UI thread; in such cases, you want to avoid blocking the UI(leading to "hangs", "Not Responding", title bars, etc). Now notice the pattern for BeginRead and EndRead:

You see, the BeginInvoke starts the asynchronous call to the method, and then the infrastructure queues (puts on sort of waiting list) the method to run on a thread pool thread and create synchronization objects as needed to determine if the method is completed. Now one thing to note here. You are very correct. The default number of threads that the thread pool can create is 25, but that is a minimum number. I believe that maximum number is 1000 threads. But the thread pool has a way of keeping track of threads in an application, so as to start eliminating threads during an application's time where there is less activity; the thread pool will start to create more threads should the application get busier in its execution (perhaps by more user interaction, etc). Having said that, the technical guru’s also content that they try to avoid using the Asynchronous Programming Model for the very reason that you have divide your application code into callback sections.
Strangely though however, my purpose in this article was to show an example of control. BeginInvoke, something that does not use a matching EndInvoke, but rather is meant for Windows Forms applications where a control will require a callback delegate that will takes some time to finish its handling routine. There is an ongoing question mark about its usage if you Google it. But the name of the game is to avoid having that UI freeze
Thanx.

I personally pass an AsyncOperation into the async method and use PostOperationCompleted to post back to the correct thread. I'm not sure if there's any advantage other than it's a little easier. I came across it examining the source code to MS's BackgroundWorker which uses it to place the RunWorkerCompleted event on the correct thread.

DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)Why are you using VB6? Do you hate yourself? (Christian Graus)