Asynchronous Programming in C#
Quite often when writing GUI apps, (especially ones that do things over the network), it is desirable to call a synchronous method asynchronously. When you create a delegate in C#, the compiler generated delegate derived class contains a BeginInvoke and EndInvoke functions that let you do this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Runtime.Remoting.Messaging;
namespace NetAsyn
{
public partial class Form1 : Form
{
private delegate string LongOperationDelegate(string longOpInput);
private delegate void LongOperationProcessOutputDelegate(string longOpOutput);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
AsynchronousExample();
}
private void SynchronousExample()
{
WebClient wc = new WebClient();
tb_Output.Text = wc.DownloadString(tb_Input.Text);
}
private void AsynchronousExample()
{
// Create the object that we want to pass to LongOperation
string longOpInput = tb_Input.Text;
// Create delegate for method that will do the work
LongOperationDelegate MyDelegate = new LongOperationDelegate(LongOperation);
// Create callback for method that will run when work is finished
AsyncCallback longOpCallback = new AsyncCallback(LongOperationCallback);
// Start work with delegate's BeginInvoke method (LongOperation's arguements come first, then callback and state object
// NB: State object can be anything you want to pass to the callback
IAsyncResult result = MyDelegate.BeginInvoke(longOpInput, longOpCallback, null);
// We could use result to call EndInvoke here (would block until work finished) or to repeatedly poll work thread
// Since we're using callback instead, result isn't used
}
private string LongOperation(string longOpInput)
{
WebClient wc = new WebClient();
return wc.DownloadString(longOpInput);
}
private void LongOperationCallback(IAsyncResult result)
{
// This method is called by LongOperation just before the thread returns, which means it is called on a separate thread to the GUI
// Get a reference to the delegate (alternatively make the delegate global or pass it as the state object - last object of BeginInvoke)
LongOperationDelegate longOperationDelegate = (LongOperationDelegate)((AsyncResult)result).AsyncDelegate;
// Get the result of the work by calling EndInvoke
string longOpOutput = longOperationDelegate.EndInvoke(result);
// Because this method is called on a separate thread to the GUI, we have to use InvokeRequired tests to access GUI objects
LongOperationProcessOutput(longOpOutput);
}
private void LongOperationProcessOutput(string longOpOutput)
{
// Because this method is called on a separate thread to the GUI, we have to use InvokeRequired to access GUI objects
if (this.InvokeRequired)
{
// Create a delegate for this method, to execute back on the GUI thread
LongOperationProcessOutputDelegate processLongOpDelegate = new LongOperationProcessOutputDelegate(LongOperationProcessOutput);
// Invoke the delegate, passing in the output from the work thread
this.Invoke(processLongOpDelegate, longOpOutput);
}
else
{
// Do the actual processing (we are now in the GUI thread)
tb_Output.Text = longOpOutput;
}
}
}
}

Intro