KnowDotNet NetRefactor

Asynchronous Programming - Part I

by William Ryan
Print this Article Discuss in Forums

Recently, I was teaching myself network programming with .NET and it really showed me how little I knew about a lot of things.  At just about every turn, I ran into something I didn't know about and couldn't use any traditional methods to deal with them.  I have a fair understanding of Threading and really like it, which helped me out with networking protocols that were blocking.  Nonetheless, I realized I needed to really spend some quality time reinforcing my knowledge of Asynchronous Programming.  It's not inherently harder or more complicated than most other issues, but you absolutely have to understand what you are doing, why you are doing it and the ramifications of it. If you are inclined to program so that you 'just get it to work', then you really need to stay away from threading (actually, you probably need to rethink your philosophy and then tackly threading) and asynchronous programming.  If you are looking for a 'quick fix' and don't want to learn about asynchronous programming, then please, don't read any more of my article, I'd prefer not to be associated in any way with the mess you'll make.  This is a pretty complex topic and there's a lot to it, so I'm going to tackle it piece by piece.

Think about any visual langauge you may have used.  They all probably have a Button of some sort and the button has a click event.  You know that any code you put behind the click will execute when the user clicks on the given button.  It's one of the first things you'll probably learn.  Well, how do you think this happens?  The program doesn't care when you click it and you are free to click any enabled control.  From a procedural standpoint, this is a big shift from the old days when something happened, then something else happened then something else and there were limited ways the program could begin.  Well, all event handlers in .NET are implemented via EventHandler Delegates.  So what is a delegate?  Well, the simple answer is a Delegate is simply an object that refers to another object's method.  Conceptually it's similar to a function pointer in C, but if you haven't programmed in C, that probably doesn't clear it up much.  Anyway, a delegate is something you can use to call the method it references.  Why not just call the method directly?  I'll show you shortly.

The first thing you need to know about delegates is that they must match the signature of what they match.  Hence, if they reference a method that takes no parameters, then they can take no parameters. If they reference a method that has one string parameter, than they must have one string parameter.  So, let's say we have the following method of some object:

Private Sub NotifyUser(ByVal Message As String, ByVal Title As String)
  
For i As Integer = 0 To 100000
      
Me.Text = "Currently Counting: " & i.ToString
     Application.DoEvents()
'I Did this on purpose to slow things down for illustration
  
Next
End
Sub


To create a delegate that references this method, we'll basically just declare a sub, preface it with the word
Delegate and finish it with the AddressOf operator pointing to the method we want to call.

Dim delegateVariable As GenericMethod = AddressOf NotifyUser


I declared this Delegate in the same class as the method, but if NotifyUser was a method of some other class, say the instance was named TestObject, then we'd use
AddressOf TestObject.NotifyUser.  Now, if we want to use the delegate instead of calling the method directly, we declare in instance of the delegate and call its Invoke method.  Assuming that I declared the above delegate with module level scope and I wanted to invoke NotifyUser when Button1 is clicked, here's the code to do it:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
   delegateVariable.Invoke("Delegates Are Cool", "Delegate Sample")
   MessageBox.Show("Done")
End Sub


So what happens if you compile this and click button1?  Well, you'll see the form's Text property display "Currently Counting: " and the number of the loop.  When it's done, a MessageBox will pop up with the text "Done".  If you're paying attention you are probably wondering what's different about using a delegate and calling the method directly, it sounds like the result(s) is/are the same, but you don't have to go through the extra work of declaring a Delegate and you can just call it directly.  The answer is Nothing, there's no visible difference.  But if that's all there was to delegates, more work for the same result, then Microsoft wouldn't have included them and I wouldn't be writing about them.

So, let's change the scenario a little bit.  We know that in the first scenario, the function will count to 100,000 changing the text property at each pass and display a messagebox when it's done.  We also know that if we have a Button2 or anything else, the form will freeze up and we won't be able to click elsewhere.  If we have DoEvents, we can click out of the form and come back in without it being 'whited out' but it's still very lame if the user wants to do anything else.  Instead of using the for loop, let's use a while loop that continues while x is less than 100,000 and a boolean variable
Continue is true.  On button2 which we'll call Cancel, we'll have some code that sets Continue to False.  Under the first scenario, it doesn't matter b/c while the first routine is running, we can't click button2 to change the value of Continue.  But let's make this modification:

Dim Continue As Boolean = True 'Add this declaration at the module level

Private Sub NotifyUser(ByVal Message As String, ByVal Title As String)
  
Dim x As Integer = 0
  
While x < 100000 And Continue
      
Me.Text = "Currently Counting: " & x.ToString
       x += 1
  
End While
End
Sub


Now, we'll change the call using the BeginInvoke method:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim AsynResult As IAsyncResult = delegateVariable.BeginInvoke("Delegates Rule", "Delegate Sample", Nothing, Nothing)
MessageBox.Show("Done")
End Sub


We'll add some code for Button2 so that we can set Continue to false:

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
   Continue =
False
End
Sub


The process will run exactly like before, except that you will be able to click around.  Immediately after you click Button1, you'll see the MessageBox with "Done" in it indicating that NotifyUser didn't block execution.  Then, if you click Button2, Continue will be set to false, the while condition will no longer be true and execution will stop.  There's a few other things that need mentioned here and this isn't by any means "all there is to it" when it comes to Asynchronous programming.

Anyway, when you use Invoke with a delegate, you are invoking it Synchronously, which means it blocks everything else until its done.  Synchronous execution with delegates is still powerful but synchronous programming as a whole is limited. Imagine a popular program like Outlook only being able to execute Synchronously.  If you were typing a long message, you'd have to manually hit Send/Recieve to get your messages.  Notifications for upcoming meetings would be totally worthless, and there'd be no way to have Outlook tell you that new mail had just arrived while you were doing something else.  I've gotten into this discussion before and died in the wool neanderthals will argue that a Timer will accomplish what you need and hence, you don't need threading or Asynchronous delegates, while others argue that using the Timer class is in fact using multiple threads.  To some degree, this argument is so silly it's not worth addressing.  However I've heard this from quite a few programmers so I figured I'd bring it up (and yes, it does fit in to the rest of the discussion).  First off, using Timers IS NOT using threaded apps.  They run in the same thread as the form they are created in.  You'll notice that there is also a Timer class in the threading library. This should be the first tip off that there's a difference.  If you read the documentation on the timer class, I think it says all that needs to be said:

"A Timer is used to raise an event at user-defined intervals. This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.

When using this timer, use the Tick event to perform a polling operation or to display a splash screen for a specified amount of time. Whenever the Enabled property is set to true and the Interval property is greater than zero, the Tick event is raised at intervals based on the Interval property setting.

This class provides methods to set the interval, and to start and stop the timer.


Anyway, I think this makes it pretty clear that using a Timer is not multithreading and hence, it's applications are limiting.  Why?  Mainly because of the same issue that I raised with the Invoke method.  If you have a method that runs longer than the interval between timer ticks, you're app will still get sluggish and non-responsive.  The only real way around this is using a seperate thread than the main one.

Back to Asynchronous methods...  When you call something Asynchronously you are allowing the code after the invocation to execute irrespective of the time it takes for the async delegate to execute.  Think of splash screens that pop up for long running operations showing you the progress of  the operation and possibly allowing you to hit a Cancel button if it's taking to long.  If you don't use Async methods, then the process will block you from hitting the cancel button so this pretty much is useless.

Every delegate contains two methods (BeginInvoke & EndInvoke), regardless of whether or not you use them.  If you use Invoke, you aren't going to call the delegate asynchronously.  To use BeginInvoke, you need to pass in multiple parameters.  The first is the actual parameters the method the delegate is pointing to takes.  If you look at the example above, we had two parameters which were both strings representing the message and title of the messagebox we were calling in NotifyUser.  If we had three parameters, then we would have had to pass in three strings first.  The next is a callback delegate which, if used, can be invoked the the primary delegate is finished its processing.  The last one is a parameter that should be fed into the callback, if it requires one.  In our example, we weren't using callbacks (we will later on in the article) so the last two parameters weren't needed.  As such, I just passed in "nothing" for each of them.

Now, you'll notice that on the line where we called BeginInvoke, we declared an instance of IAsyncResult and set it to the BeginInvoke.  As you can guess, BeginInvoke generically returns an IAsyncResult object.  Without this, we wouldn't be able to process the method asynchronously.  In this relatively simple example, we didn't need any information from our method, after all it just ran some base processing.  However, what if this was some mathematics program that did some really complex calculations that took 15 minutes.  We want to program to be able to do other things while this is happening but if we can't get back the value of the calculation , it's pretty much worthless. We may also want some sort of notification that the method completed successfully, and that's where Callbacks come in.

So, when we call the BeginInvoke method what happens?  We don't see any ThreadStart or such invocation so is this really multithreading?  Yes.  One of the thing the CLR does is manage a thread pool and distribute threads as they are requested provided they are available.  There is no guarantee that there will be a free thread, and if there isn't, the request is stored in an internal Queue.  When/If a thread becomes available, the delegate is exectued on that thread until its execution is complete.  The IAsyncResult is the mechanism by which any delegate results are returned.  In our example there aren't any results so 'nothing' is returned. To prove this, I put a breakpoint on the MessageBox.Show line and right before it, I added this line:

    Debug.WriteLine(AsynResult)

{System.Runtime.Remoting.Messaging.AsyncResult}
    [System.Runtime.Remoting.Messaging.AsyncResult]: {System.Runtime.Remoting.Messaging.AsyncResult}
    AsyncState: Nothing
    AsyncWaitHandle: {System.Threading.ManualResetEvent}
    CompletedSynchronously: False
    IsCompleted: False

This is just a fancy way of saying that AsynResult (the instance of our IAsyncResult) is nothing.  Now that we know what BeginInvoke does, what does EndInvoke do?  One natural assumption is that it's used to prematurely end whatever you called in BeginInvoke.  Unfortunately, that's not the case.  If you want to end the execution of the delegate, you'll need to raise an event to stop it or use a mechanism like the boolean that we used above.  This can get pretty complex so I'm not going to get into it until Part II of this article.  Back to EndInvoke.  When your method from BeginInvoke finishes executing, EndInvoke is called.  It takes the IAsyncResult that was used in the BeginInvoke and returns the value from your method/delegate call if there is one.  It's important to note that many times there won't be any result.  In our case, we were doing some routine with a sub, in C# a void.  However, there will be times that values will be caculated that take a long time  or you need return codes, and in those instances, you will have a return value.  Anyway, the other use for EndInvoke is if you don't want Asynchronous execution to continue on Asynchronously.  Remember in the above example that as soon as the button1 was pressed, we called NotifyUser via our delegate Asynchronously and the MessageBox popped up with "Done" even though the procedure was still running.  This proved that NotifyUser wasn't blocking the rest of the procedure.  However, if I add an EndInvoke before the call the the MessageBox, then NotifyUser will block execution and the MessageBox won't be shown until the counting routine is finished:

No Blocking (MessageBox is Shown Immediately)

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim AsynResult As IAsyncResult = delegateVariable.BeginInvoke("Delegates Rule", "Delegate Sample", Nothing, Nothing)
MessageBox.Show("Done")
End Sub


Blocking by delegateVariable/NotifyUser (MessageBox not shown until NotifyUser finishes execution)

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  
Dim AsynResult As IAsyncResult = delegateVariable.BeginInvoke("Delegates Rule", "Delegate Sample", Nothing, Nothing)
  delegateVariable.EndInvoke(AsynResult)
  MessageBox.Show("Done")
End Sub


When I first came across this, I found it a bit counterintuitive.  There's no straightforward way to end execution of the process but at first, I thought EndInvoke would do it or signal that everything should end.  At that point, I thought, what possible use would this have?  If I want something to run Asynchronously and have the UI remain responsive so the user can cancel it for instance, why would I want to remove that functionality?  One example I can think of is transactional scenarios.  You may have a process that completes in steps.  At the beginning phases, you may want to user to be able to cancel the processing and do something else.  However at some point, you may want to force the user to accept the process and thereby cancel the ability to cancel the processing.  While scenarios like this are usually handled with message queues or more sophsiticated methods, this will work in a pinch.  But, there are more elegant ways to get this functionality.  A better example is when you are waiting for a return value.  Just because the process finished doesn't mean you'll get your return value.  So, you may want to use polling coupled with the IsCompleted property to determine the end of the processing, then call EndInvoke to grab your value.  All you'd do is make your call, the use a While loop with .IsCompleted as the condition, then use EndInvoke.  Here's a good example.

First, create a Delegate:

Public Delegate Function CompletedExampleDelegate(ByVal FirstVal As Integer, ByVal SecondVal As Integer) As Integer

Next, create a function with the same signature:

Private Function mySum(ByVal FirstVal As Integer, ByVal SecondVal As Integer) As Integer
    Return FirstVal + SecondVal
End Function


After this, create  a Stub/Sub to pass the EndInvoke Value to:

Private Sub ShowSum(ByVal FirstVal As Integer, ByVal SecondVal As Integer)
    
Me.Text = FirstVal + SecondVal
End Sub

Now create the EndInvoke Routine:

Private Sub MakeValues(ByVal myValue As Integer)
    
Me.Text = myValue
End Sub

Finally, use the CreateInvoke to do the processing...then call the Sub passing in EndInvoke Value:

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
  
Dim SumResult As IAsyncResult = BeginInvoke(New CompletedExampleDelegate(AddressOf mySum), New Object() {50, 100})
  
While (Not Result.IsCompleted)
       Application.DoEvents()
'Must have this or loop will never exit because it has
       'to work through message pump
   End While
   MakeValues(EndInvoke(SumResult))
End Sub


This may look a little confusing once you get into the Button3_Click so let me highlight a few things.  First, we created a new IAsyncResult which is nothing new.  We called the BeginInvoke fuction passing in a new instance of the Delegate which is pointing to mySum.  This however doesn't have to point to mySum.  Any method with the same signature will work, and that's the beauty of delegates.  Next we create a new Object array and pass in the two values.  Why Two?  50 & 100 are going to be the parameters we use for the sum function.

Now, we test for Not Result.IsCompleted which will happen indefinitely as long as the method is executing.  You'll notice the Application.DoEvents.  Just to get a feel for how things are really working, go ahead and compile this code, but comment out the DoEvents line.  You'll notice that you never hit the MakeValues line.  Remember what happens when we use IAsyncResult.  The CLR looks to the thread pool and grabs a new one when/if it's available.  The Windows Message Pump is holding these messages (after all, they're in a different thread) and you need to call DoEvents to release them.  Different situations may necessitate different methods but in this case, DoEvents gets it done.

So far, we've barely touched the surface the Asynchronous programming but we've laid the ground work for some more serious discussion.  In the next article, I'm going to finish up Delegates using Wait, WaitHandle and WaitOne.  I'll touch upon a few objects that have inherent support for asynchronous processing and then begin a discussion of Threading.  Threading is a subject you can write entire books on, so it will take a few articles to fully discuss it.  However, it's definitely worth discussing and it's a very interesting topic.  If you have any questions on this, please don't hesitate to let me know.  I can be reached here

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing