Asynchronous Programming in C#

June 4th, 2006 by Patrick Leave a reply »

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;
}
}
}
}
Advertisement

Comments are closed.