Friday, November 21, 2008

The Asynchronous Programming Model (APM)

"A synchronous method call waits for the method to complete before continuing with program flow, whereas an asynchronous method call will return immediately so that the program can perform other operations while the called method completes its work. "

The asynchronous design pattern used in the .NET Framework is characterized by the use of BeginXXXX and EndXXXX methods, where XXXX is the name of the synchronous version of the method.

For example when dealing with file stream, there is BeginRead/EndRead method:

ex 1.

byte[] buffer=new byte[100];
string filename=...;
FileStream strm=...;

//Make asynchrounous call
IAsyncResult result=strm.BeginRead(buffer,0,buffer.Lentgh,null,null);

//do some work here while you wait

//Calling EndRead will block until the Async work is complete
int numBytes=strm.EndRead(result);

strm.Close();

The BeginRead is similar to Read Method, but it returns an IAsyncResult instead of the number of bytes read. At the end of the operation, you will call the EndRead with the IAsyncResult object and it will return the byetes read.

There are 3 styles of models to do asynchrous call, wait-until-done model(ex 1), polling model( similar to wait-until-done model, except in the "main" thread, call IsCompleted property on IAsyncResult object to see if complete), and callback mode.

Let's look at Callback Model:

ex 2.

static byte[] buffer=new byte[100];

static void TestCallbackAPM()
{
string filename=...;
FileStream strm=...;

//Make asynchronous call
IAsyncResult result=strm.BeginRead(buffer,0,buffer.Length, new AsyncCallBack(CompleteRead), strm);
}

static void CompleteRead(IAsyncResult result)
{
FileStream strm=(FileStream)result.AsyncState;

//finished, we can call EndRead and it will return without blocking
int numBytes=strm.EndRead(result);

strm.Close();
}

In our silverlight and ADO.net data service learning, we have similar code, they are using the callback model:

ex 3.
//Get Data
private void ButtonSelect_Click(object sender, RoutedEventArgs e)
{
ServiceReference.DataEntities ws = new ServiceReference.DataEntities(new Uri("WebDataService.svc", UriKind.Relative));
var query = (from p in ws.DataSources select p);
DataServiceQuery userQuery = (DataServiceQuery)query;
userQuery.BeginExecute(new AsyncCallback(OnLoadComplete), query);
}
void OnLoadComplete(IAsyncResult result)
{
DataServiceQuery query = (DataServiceQuery)result.AsyncState;
dataGrid.ItemsSource = query.EndExecute(result).ToList();
}


//Insert Record by Text Box
private void InsertClick(object sender, RoutedEventArgs e)
{
ServiceReference.DataEntities ws = new ServiceReference.DataEntities(new Uri("WebDataService.svc", UriKind.Relative));
DataSources ds = new DataSources();
ds.ProductID = Int32.Parse(PIDText.Text);
ds.SourceID = SIDText.Text;
ds.SourceGroupID = SGIDText.Text;
ds.SourceName = SNText.Text;
ws.AddToDataSources(ds);
ws.BeginSaveChanges(OnSaveChangesCompleted, ws);
}

void OnSaveChangesCompleted(IAsyncResult result)
{
ServiceReference.DataEntities wsd = (ServiceReference.DataEntities)result.AsyncState;
ObservableCollection obds = new ObservableCollection();
obds.Add(result.AsyncState as DataSources);
wsd.EndSaveChanges(result);
}

No comments: