Introduction
Today, line of Internet and multimedia-enabled SmartPhones have become popular. Fortunately, if you are a .NET developer, you can use your existing skills and language and target some of the most popular mobile Operating Systems.
Windows Phone 7 is a mobile Operating System developed by Microsoft, and is the successor to their Windows Mobile platform.
Background
You can expose asynchronous features to client code using one of the following .NET Framework Asynchronous Programming models:
IAsyncResult
The CLR's Asynchronous Programming Model (APM)
- Event-based Asynchronous Pattern (EAP)
IAsyncResult
has been around since .NET 1.0, and has been used in most BCL classes, while the main benefit of the EAP is that it integrates with the Visual Studio UI designers. You can learn how to properly implement the IAsyncResult
interface (APM) from this excellent article on MSDN: Implementing the CLR Asynchronous Programming Model, by Jeffrey Richter.
In this post, I will show you how easy it is to use types implementing the IAsyncResult
interface on Windows Phone 7. I will use the PowerThreading library[1] because it provides a similar (not to say an improved) implementation of the one described in the original MSDN article. I will explain how to use it and how this becomes easier using the AsyncEnumerator
class.
The Problem
The code linked to above is used for the demo. This code does some I/O which causes a thread to block.
public interface IWebService
{
IStockQuote FetchStockQuotes();
}
internal sealed class WebService : IWebService
{
private readonly IStockQuote m_quotes;
public WebService(IStockQuote quotes)
{
m_quotes = quotes;
}
#region IWebService Members
public IStockQuote FetchStockQuotes()
{
Thread.Sleep(50); return m_quotes;
}
#endregion
}
Sync I/O Pauses the UI
The code below shows how the ExecuteWithSyncIO
method is implemented. The application shows a MessageBox
to the user that the UI will pause while the execution is in progress.
private void ExecuteWithSyncIO()
{
for (Int32 n = 0; n < c_iterations; n++)
{
m_webService.GetStockQuotes();
}
SetStatus("Sync/IO completed.", StatusState.Ready);
}
Delegate's BeginInvoke Method is Not Supported
The code below shows how the ExecuteWithDelegateBeginInvoke
method is implemented.
This method is here just for the demo, since it is not allowed to invoke a delegate asynchronously in the Compact Framework.
private void ExecuteWithDelegateBeginInvoke()
{
Func<IStockQuote> stockQuoteDelegate = m_webService.GetStockQuotes;
stockQuoteDelegate.BeginInvoke((ar) => {
stockQuoteDelegate.EndInvoke(ar);
}, null);
}
The Solution
The Wintellect.Threading.AsyncProgModel.AsyncResult<TResult>
class contains an implementation of the IAsyncResult
interface. This type is generic and we can easily use it on the WebService
class.
using System;
using System.Threading;
using Wintellect.Threading.AsyncProgModel;
public interface IWebService
{
IStockQuote FetchStockQuotes();
}
internal sealed class WebService : IWebService
{
private readonly IStockQuote m_quotes;
public WebService(IStockQuote quotes)
{
m_quotes = quotes;
}
public IAsyncResult BeginGetStockQuotes(AsyncCallback callback, Object state)
{
AsyncResult<IStockQuote> ar =
new AsyncResult<IStockQuote>(callback, state);
ThreadPool.QueueUserWorkItem(GetStockQuotesHelper, ar);
return ar; }
public IStockQuote EndGetStockQuotes(IAsyncResult asyncResult)
{
AsyncResult<IStockQuote> ar =
(AsyncResult<IStockQuote>)asyncResult;
return ar.EndInvoke();
}
private void GetStockQuotesHelper(Object state)
{
AsyncResult<IStockQuote> ar = (AsyncResult<IStockQuote>)state;
try
{
IStockQuote quotes = FetchStockQuotes();
ar.SetAsCompleted(quotes, false);
}
catch (Exception e)
{
ar.SetAsCompleted(e, false);
}
}
#region IWebService Members
public IStockQuote FetchStockQuotes()
{
Thread.Sleep(5); return m_quotes;
}
#endregion
}
Actually, the GetStockQuotesHelper
method could be inlined. I try to avoid inlined delegates because you can easily access variables defined in the parent method body.
Let's look now at how the above can be used on Windows Phone 7.
IAsyncResult Interface
The code below shows how the ExecuteWithIAsyncResult
method is implemented. The only problem is that, when using the IAsyncResult
, you need to specify a method to be called when a corresponding asynchronous operation completes. This can result in using synchronization constructs to avoid race conditions. It also splits the flow of your code. You can inline the callback method using Anonymous Methods or Lambda Expressions as shown below, but if your logic is complicated, your code will not be beautiful.
private void ExecuteWithIAsyncResult()
{
SetStatus("Working..", StatusState.Busy);
for (Int32 n = 0; n < c_iterations; n++)
{
m_webService.BeginGetStockQuotes((ar) => {
if (Interlocked.Increment(ref m_numDone) == c_iterations)
{
Execute.OnUIThread(() => {
SetStatus("IAsyncResult APM completed.",
StatusState.Ready);
});
}
}, null);
}
}
AsyncEnumerator Class
The code below shows how the ExecuteWithAsyncEnumerator
method is implemented. As you can see, this method makes your code looks like it's executing synchronously, but actually it executes asynchronously. You do not have to split your code in callback methods or inlined delegates. You do not need to marshal calls in the UI thread using the Dispatcher
or the SynchronizationContext
. All this stuff is handled by the AsyncEnumerator
class.
private IEnumerator<Int32> ExecuteWithAsyncEnumerator(AsyncEnumerator ae)
{
for (Int32 n = 0; n < c_iterations; n++)
{
m_webService.BeginGetStockQuotes(ae.End(), null);
}
ae.SyncContext = null;
yield return c_iterations;
for (Int32 n = 0; n < c_iterations; n++)
{
m_webService.EndGetStockQuotes(ae.DequeueAsyncResult());
}
SetStatus("AsyncEnumerator completed.", StatusState.Ready);
}
Points of Interest
While what I've discussed in this post applies to Mobile application development, the same principles can be applied to Rich Internet applications and Smart Clients. I have been using the AsyncEnumerator
class for over two years, and I have to say that it changed the way I think about using the APM. In the end.. delivering responsive applications makes the end-users happy.
More samples can be found on my GitHub repository.
References
- AsyncEnumerator class resides in the PowerThreading library. It was written by Jeffrey Richter and can be obtained from the Wintellect website.