How to call a Visual C# method asynchronously (315582)



The information in this article applies to:

  • Microsoft Visual Studio .NET (2002), Professional Edition
  • Microsoft Visual C# .NET (2002)
  • Microsoft Visual C# 2005, Express Edition

This article was previously published under Q315582

SUMMARY

The Microsoft .NET Framework makes it easy to call functions asynchronously. Calling functions asynchronously causes the system to execute them in the background on a secondary thread while the calling function continues to do other work. In a typical (synchronous) function call, the function is executed right away on the same thread that made the call. The calling function waits for the call to complete and receives the results of the call before continuing. By contrast, when you make an asynchronous call, you retrieve the results of the asynchronous call later. This article demonstrates how to do this by using Visual C#.

back to the top

Requirements


The following list outlines the recommended hardware, software, network infrastructure, and service packs that are required:
  • Microsoft Windows 2000 or Microsoft Windows XP or Microsoft Windows Server 2003
  • Visual Studio .NET or Visual Studio 2005
This article assumes that you are familiar with the following topics:
  • Calling methods in Visual C#
  • How to use delegates
back to the top

How To Make Asynchronous Calls

Asynchronous calls are made by using delegates. A delegate is an object that wraps a function. Delegates provide a synchronous function and also provide methods for calling the wrapped function asynchronously. Those methods are BeginInvoke() and EndInvoke(). The parameter lists of these methods vary depending on the signature of the function that the delegate wraps. Note that the Visual Studio .NET IntelliSense feature does not display BeginInvoke() and EndInvoke(), so you do not see them appear in the function lists as you type.

BeginInvoke() is used to initiate the asynchronous call. It has the same parameters as the wrapped function, plus two additional parameters that will be described later in this article. BeginInvoke() returns immediately and does not wait for the asynchronous call to complete. BeginInvoke() returns an IAsyncResult object.

The EndInvoke() function is used to retrieve the results of the asynchronous call. It can be called anytime after BeginInvoke(). If the asynchronous call has not completed yet, EndInvoke() blocks until it completes. The parameters of the EndInvoke() function includes the out and ref parameters that the wrapped function has, plus the IAsyncResult object that is returned by BeginInvoke().

The following is an example of a delegate and its BeginInvoke() and EndInvoke() methods:
// The following delegate 
delegate string MethodDelegate(int iCallTime, out int iExecThread)  ;

// will have the following BeginInvoke() and EndInvoke methods:
IAsyncResult MethodDelegate.BeginInvoke(int iCallTime, out int iExecThread, AsyncCallback cb, object AsyncState); 

string MethodDelegate.EndInvoke (out int iExecThread, IAsyncResult ar) ;
				
There are four common ways to use BeginInvoke() and EndInvoke() to make asynchronous calls. After you call BeginInvoke(), you can:
  • Optionally do some other work and then use EndInvoke().
  • Obtain a WaitHandle that is provided by the IAsyncResult object, use its WaitOne method to block until the WaitHandle is signaled, and then call EndInvoke().
  • Poll the IAsyncResult object to determine when the asynchronous call has completed, and then call EndInvoke().
  • Have the system call a callback function that you specify. This callback function calls EndInvoke() and processes the results of the asynchronous call when it completes.
The following code samples demonstrate these call patterns and contrast them with making a synchronous call by using the following function:
string LongRunningMethod (int iCallTime, out int iExecThread)
{
	Thread.Sleep (iCallTime) ;
	iExecThread = AppDomain.GetCurrentThreadId ();
	return "MyCallTime was " + iCallTime.ToString() ;
}

				
LongRunningMethod() simulates a function that runs for long time by sleeping. It returns the sleep time and the ID of the thread that executes it. If you call it asynchronously, you find that the thread ID of the executing thread is different from that of the calling thread.

The first step is to define the delegate that wraps the function:
delegate string MethodDelegate(int iCallTime, out int iExecThread)  ;
				

Sample 1: Calling A Method Synchronously

This sample demonstrates how to call LongRunningMethod() synchronously by using a MethodDelegate delegate. The other samples contrast this by making calls asynchronously.
  1. Start Microsoft Visual Studio .NET or Microsoft Visual Studio 2005.
  2. Create a new Visual C# Console Application project named AsyncDemo.
  3. Add a class named AsyncDemo that is defined as follows to the project in a new .cs file:
    using System;
    using System.Threading ; 
    using System.Windows.Forms ;
    
    public class AsyncDemo
    {
    	string LongRunningMethod (int iCallTime, out int iExecThread)
    	{
    		Thread.Sleep (iCallTime) ;
    		iExecThread = AppDomain.GetCurrentThreadId ();
    		return "MyCallTime was " + iCallTime.ToString() ;
    	}
    
    	delegate string MethodDelegate(int iCallTime, out int iExecThread)  ;
    
    	public void DemoSyncCall()
    	{
    		string s ;
    		int iExecThread;
    
    		// Create an instance of a delegate that wraps LongRunningMethod.
    		MethodDelegate dlgt = new MethodDelegate (this.LongRunningMethod) ;  
    			
    		// Call LongRunningMethod using the delegate.
    		s = dlgt(3000, out iExecThread);  
    			
    		MessageBox.Show (string.Format ("The delegate call returned the string:   \"{0}\", 
                                            and the thread ID {1}", s, iExecThread.ToString() ) );
    	}
    }
    					
    Later, this class demonstrates how to make asynchronous calls. Initially, however, it only contains the DemoSyncCall() method, which demonstrates how to call the delegate synchronously.
  4. Add the following code in the body of the Main function that Visual Studio automatically creates in your project:
    static void Main(string[] args)
    {
    	AsyncDemo ad = new AsyncDemo () ;
    	ad.DemoSyncCall() ;
    }
    					
  5. Press CTRL+F5 to run your application.
back to the top

Sample 2: Calling A Method Asynchronously by Using the EndInvoke() Call Pattern

In this section, the sample invokes the same method asynchronously. The call pattern that is used is to call BeginInvoke, do some work on the main thread, and then call EndInvoke(). Note that EndInvoke() does not return until the asynchronous call has completed. This call pattern is useful when you want to have the calling thread do work at the same time that the asynchronous call is executing. Having work occur at the same time can improve the performance of many applications. Common tasks to run asynchronously in this way are file or network operations.
  1. Add a method named DemoEndInvoke() to the AsyncDemo class. The DemoEndInvoke function demonstrates how to call the delegate asynchronously.
    public void DemoEndInvoke()
    {
    	MethodDelegate dlgt = new MethodDelegate (this.LongRunningMethod) ;
    	string s ;
    	int iExecThread;
    
    	// Initiate the asynchronous call.
    	IAsyncResult ar = dlgt.BeginInvoke(3000, out iExecThread, null, null);  
    
    	// Do some useful work here. This would be work you want to have
    	// run at the same time as the asynchronous call.
    
    	// Retrieve the results of the asynchronous call.
    	s = dlgt.EndInvoke (out iExecThread, ar) ;  
    
    	MessageBox.Show (string.Format ("The delegate call returned the string:   \"{0}\", 
                                   and the number {1}", s, iExecThread.ToString() ) );
    }
    					
  2. Edit the source code for Main so that it contains the following code:
    static void Main(string[] args)
    {
    	AsyncDemo ad = new AsyncDemo () ;
    	ad.DemoEndInvoke() ;
    }
    					
  3. Press CTRL+F5 to run your application.
back to the top

Sample 3: Calling A Method Asynchronously And Using A WaitHandle To Wait For The Call To Complete


In this section, the sample calls the method asynchronously and waits for a WaitHandle before it calls EndInvoke(). The IAsyncResult that is returned by BeginInvoke() has an AsyncWaitHandle property. This property returns a WaitHandle that is signaled when the asynchronous call completes. Waiting on a WaitHandle is a common thread synchronization technique. The calling thread waits on the WaitHandle by using the WaitOne() method of the WaitHandle. WaitOne() blocks until the WaitHandle is signaled. When WaitOne() returns, you can do some additional work before you call EndInvoke(). As in the previous sample, this technique is useful for executing file or network operations that would otherwise block the calling main thread.
  1. Add a function named DemoWaitHandle() to the AsyncDemo class. The DemoWaitHandle() function demonstrates how to call the delegate asynchronously.
    public void DemoWaitHandle ()
    {
    	string s ;
    	int iExecThread;
    
    	MethodDelegate dlgt = new MethodDelegate (this.LongRunningMethod) ;
    
    	// Initiate the asynchronous call.
    	IAsyncResult ar = dlgt.BeginInvoke(3000, out iExecThread, null, null); 
    
    	// Do some useful work here. This would be work you want to have
    	// run at the same time as the asynchronous call.
    
    	// Wait for the WaitHandle to become signaled.
    	ar.AsyncWaitHandle.WaitOne() ;
    
    	// Get the results of the asynchronous call.
    	s = dlgt.EndInvoke (out iExecThread, ar) ;
    			
    	MessageBox.Show (string.Format ("The delegate call returned the string:   \"{0}\", 
                                     and the number {1}", s, iExecThread.ToString() ) );
    }
    					
  2. Edit the source code for Main so that it contains the following code:
    static void Main(string[] args)
    {
    	AsyncDemo ad = new AsyncDemo () ;
    	ad.DemoWaitHandle () ;
    }
    					
  3. Press CTRL+F5 to run your application.
back to the top

Sample 4: Calling A Method Asynchronously by Using the Polling Call Pattern

In this section, the sample polls the IAsyncResult object to find out when the asynchronous call has completed. The IAsyncResult object that is returned by BeginInvoke() has an IsCompleted property that returns True after the asynchronous call completes. You can then call EndInvoke(). This call pattern is useful if your application does ongoing work that you do not want to have blocked by a long-running function call. A Microsoft Windows application is an example of this. The main thread of the Windows application can continue to handle user input while an asynchronous call executes. It can periodically check IsCompleted to see if the call has completed. It calls EndInvoke when IsCompleted returns True. Because EndInvoke() blocks until the asynchronous operation is complete, the application does not call it until it knows that the operation is complete.
  1. Add a function named DemoPolling() to the AsyncDemo class. The DemoPolling() function demonstrates how to call the delegate asynchronously and use polling to see if the process is complete.
    public void DemoPolling()
    {
    	MethodDelegate dlgt = new MethodDelegate (this.LongRunningMethod) ;
    	string s ;
    	int iExecThread;
    
    	// Initiate the asynchronous call.
    	IAsyncResult ar = dlgt.BeginInvoke(3000, out iExecThread, null, null); 
    
    	// Poll IAsyncResult.IsCompleted
    	while(ar.IsCompleted == false)
    	{
    		Thread.Sleep (10) ;  // pretend to so some useful work
    	}
    	s = dlgt.EndInvoke (out iExecThread, ar) ;
    
    	MessageBox.Show (string.Format ("The delegate call returned the string:   \"{0}\", 
                                    and the number {1}", s, iExecThread.ToString() ) );
    }
    
    					
  2. Edit the source code for Main. Replace the content of the function with the following code:
    static void Main(string[] args)
    {
    	AsyncDemo ad = new AsyncDemo () ;
    	ad.DemoPolling () ;
    }
    					
  3. Press CTRL+F5 to run your application.
back to the top

Sample 5: Executing a Callback When an Asynchronous Method Completes

In this section, the sample provides a callback delegate to the BeginInvoke() function that the system executes when the asynchronous call completes. The callback calls EndInvoke() and processes the results of the asynchronous call. This call pattern is useful if the thread that initiates the asynchronous call does not need to process the results of the call. The system invokes the callback on a thread other than the initiating thread when the asynchronous call completes.

To use this call pattern, you must pass a delegate of type AsyncCallback as the second-to-last parameter of the BeginInvoke() function. BeginInvoke() also has a final parameter of type object into which you can pass any object. This object is available to your callback function when it is invoked. One important use for this parameter is to pass the delegate that is used to initiate the call. The callback function can then use the EndInvoke() function of that delegate to complete the call. This call pattern is demonstrated below.
  1. Add a two methods named DemoCallback() and MyAsyncCallback() to the AsyncDemo class. The DemoCallback() method demonstrates how to call the delegate asynchronously. It uses a delegate to wrap the MyAsyncCallback() method, which the system calls when the asynchronous operation completes. MyAsyncCallback() calls EndInvoke().
    public void DemoCallback()
    {
    	MethodDelegate dlgt = new MethodDelegate (this.LongRunningMethod) ;
    	string s ;
    	int iExecThread;
    
    	// Create the callback delegate.
    	AsyncCallback cb = new AsyncCallback(MyAsyncCallback);
    
    	// Initiate the Asynchronous call passing in the callback delegate
    	// and the delegate object used to initiate the call.
    	IAsyncResult ar = dlgt.BeginInvoke(3000, out iExecThread, cb, dlgt); 
    }
    
    public void MyAsyncCallback(IAsyncResult ar)
    {
    	string s ;
    	int iExecThread ;
    
    	// Because you passed your original delegate in the asyncState parameter
    	// of the Begin call, you can get it back here to complete the call.
    	MethodDelegate dlgt = (MethodDelegate) ar.AsyncState;
    
    	// Complete the call.
    	s = dlgt.EndInvoke (out iExecThread, ar) ;
    
    	MessageBox.Show (string.Format ("The delegate call returned the string:   \"{0}\", 
                                    and the number {1}", s, iExecThread.ToString() ) );
    }
    
    					
  2. Edit the source code for Main. Replace the content of the function with the following code:
    static void Main(string[] args)
    {
    	AsyncDemo ad = new AsyncDemo () ;
    	ad.DemoCallback() ;
    }
    					
  3. Press CTRL+F5 to run your application.
back to the top

Modification Type:MajorLast Reviewed:1/18/2006
Keywords:kbHOWTOmaster KB315582 kbAudDeveloper