In-Depth
Designing and Implementing Synchronous vs. Asynchronous WCF Services
Asynchronous services are easier to implement with support of the await and async keywords in WCF 4.5. We look at that, as well as contract-first development.
There are performance benefits to using asynchronous services that Windows Communication Foundation 4.5 allows, and with that, one should know the differences between sync and async services as well as task-based async support. To do that, I'll illustrate these concepts through a sample application and also discuss the contract-first development approach in WCF 4.5. I'll also show the difference between sync and async services by implementing a synchronous and an asynchronous WCF service.
To work with the examples in this article, you should have Visual Studio 2013 or newer installed.
Why Is Asynchrony Needed?
A process is the running instance of a program. A process contains a Process Control Block (PCB) of its own. There are many threads within a process. A thread is the smallest individually schedulable entity within a process. In other words, a thread is the smallest unit within a process.
Now, applications can be single threaded or multithreaded. In a single-threaded application, the processor executes the threads sequentially. In essence, the threads are executed one after the other but all the threads have to wait until the currently executing thread completes its activity.
Let's take an example. If you have x number of threads (with x representing the thread count) to be executed by your processor in a single -threaded environment, you would have one of those threads in the execution state and the remaining x - 1 threads wait for their turn. Note that unless the execution of the currently running thread is complete, all other threads will have to wait for their turn and also, all threads will be executed sequentially.
In a single-threaded application, the threads monopolize the processor and this type of multithreading is incapable of providing much system throughput as the currently executing thread relinquishes control of the processor to the next thread only after its executing is complete in the entirety. Note that system throughput is a measure of the amount of work done in unit time.
Likewise, a multithreaded application has multiple threads residing in memory at a particular instant of time. One of these threads would be in the running or execution state, while all other threads would be in the ready or runnable state. In the running state a thread has access to all resources including the processor. In a ready or runnable state a thread doesn't have access to the processor and waits for its turn in a ready queue to be scheduled. Note that threads are scheduled by the operating system.
In a multithreaded application, a thread is preempted and scheduled as and when needed based on thread priorities. So, the executing threads cannot monopolize the processor, i.e., the executing thread can always be preempted by the operating system and placed in the ready queue and another thread with a higher priority scheduled by the operating system even before the turnaround time of the thread in execution is complete.
Now, because of the nature of its operation, single-threaded applications cannot leverage the multi-core processors. With this in mind, support for parallel programming was introduced in .NET Framework 4.0. The support for asynchronous operations in WCF 4.5 makes it very flexible as far as throughput is concerned. The ability to call a WCF 4.5 service asynchronously enables an application to continue its other operations as the main thread (the application thread) is not blocked and so it doesn't need to wait for the service operation to be complete.
Calling a WCF Service Synchronously and Asynchronously
A WCF client can call a WCF service both synchronously and asynchronously. If you want your WCF client to call your WCF service asynchronously, you need to select the "Generate asynchronous operations" checkbox when adding the service reference. This option is available in the advanced option, but not by default -- you need to click the "Advanced..." button while adding service reference. When this checkbox is checked, Visual Studio generates proxies to support both synchronous and asynchronous calls.
To invoke a WCF service operation synchronously from a WCF client you can call the proxy method generated by Visual Studio when the service reference is added. To call a WCF service method asynchronously you need to create an AsyncCallback delegate and pass this delegate to the asynchronous proxy method.
Note that the AsyncCallback delegate is executed on a worker thread and so this doesn't block the main thread of the application. Another way to call a WCF service asynchronously is to create a worker thread and then call the WCF service operation using this thread. This way you don't need to generate asynchronous proxies but you would achieve the same results.
Implementing Asynchronous Service Operations in WCF 4.5
Let's explore the simplified Asynchronous Programming Model in WCF 4.5 with async and await keywords.
In WCF 4.5 you can implement asynchronous operations in one of these three ways: Task-based, Event-based, and via IAsyncResult-based.
Task-based Asynchronous Pattern is the easiest pattern used to implement asynchronous operations. All you have to do is specify a return type of Task<T> in your service operation. Here, T represents generic type returned by the service method. This code:
public class TaskBasedAsynchronousService : ITaskBasedAsynchronousService
{
public async Task<string> GetStringDataAsync(string textData)
{
return Task<string>.Factory.StartNew(() =>
{
return textData;
});
}
}
illustrates an example of this pattern. In essence, the Task-based Asynchronous Pattern is based on Task and Task<TResult> types defined in the System.Threading.Tasks namespace.
Event-based asynchronous pattern, as the name suggests, is based on events. You need to have one or more MethodNameAsync method(s) and corresponding MethodNameCompleted event handler(s) for the service operations. This code shows how this pattern can be implemented:
public class EventBasedAsynchronousService
{
public void OperationOnStringData(string text);
public void OperationOnStringDataAsync(string text);
public void OperationOnStringDataAsync(string text, object state);
public event OperationOnStringDataCompletedEventHandler OperationOnStringDataCompleted;
public void CancelAsync(object state);
public bool IsBusy { get; }
}
With the IAsyncResult Asynchronous Pattern, a service operation can be made asynchronous by setting the AsyncPattern property to true and defining two methods: BeginOperation and EndOperation. While theBeginOperation method needs to return an IAsyncResult type, the EndOperation method should include an IAsyncResult parameter and return a type of the operations return type, like this:
[OperationContractAttribute(AsyncPattern=true)]
IAsyncResult BeginServiceAsyncMethod(string text, AsyncCallback callback, object asyncState);
string EndServiceAsyncMethod(IAsyncResult result);
Implementing a Sample Application
WCF vNext provides support for async model in both the server and the client side. It provides new Task-based overloaded methods to facilitate asynchronous programming. In a Task-based asynchronous model when you invoke a generated proxy method, a Task object is constructed that represents the asynchronous operation and this Task is returned from this service method.
Support for the Task-based asynchronous programming model has been updated in .NET 4.5 with the introduction of the keywords await and async. The asyn and await keywords in .NET 4.5 helps to avoid performance bottlenecks and enhance the overall responsiveness of your application. If a service method uses the async keyword, it implies that it is an asynchronous method and it might have an await keyword inside. If an asynchronous service method contains an await keyword, async would activate it.
In the MSDN Topic, Asynchronous Programming with Async and Await (C# and Visual Basic), it states:
"Asynchrony is essential for activities that are potentially blocking, such as when your application accesses the web. Access to a web resource sometimes is slow or delayed. If such an activity is blocked within a synchronous process, the entire application must wait. In an asynchronous process, the application can continue with other work that doesn't depend on the web resource until the potentially blocking task finishes."
Note that a WCF server or a WCF client can apply both sync or async pattern to execute the same service operation.
Let's move to the code. Here's a simple service contract that contains an asynchronous service operation:
[ServiceContract]
public interface IDemoService
{
[OperationContract(IsOneWay = false, AsyncPattern = true)]
IAsyncResult BeginDemo(string text, AsyncCallback callback, object state);
string EndDemo(IAsyncResult result);
}
Here's a code snippet that implements the IDemoService interface and illustrates how the service class looks like:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class DemoService : IDemoService
{
public IAsyncResult BeginHello(string text, AsyncCallback callback, object state)
{
DemoAsyncResult result = new HelloAsyncResult("The text message is: " + text, callback, state);
ThreadPool.QueueUserWorkItem(new WaitCallback(DemoCallbackMethod), result);
return result;
}
}
You can access the service either from an async call or a sync call at the client side. So, you can have a sync client that can access the above async operation using the following service contract that declares the service operation for a synchronous call:
[ServiceContract]
public interface IDemoService
{
[OperationContract(IsOneWay = false)]
string Demo(string text);
}
Consider the following service contract:
using System.ServiceModel;
using System.Threading.Tasks;
namespace AsyncWCF
{
[ServiceContract]
public interface IDemoService
{
[OperationContract]
Task<string> GetStringDataAsync(string name);
}
}
This service contract is implemented by the service class shown here:
using System.Threading.Tasks;
namespace AsyncWCF
{
public class DemoService : IDemoService
{
public async Task<string> GetStringDataAsync(string text)
{
return await Task.Factory.StartNew(() => "The text is: " + text);
}
}
}
Usage of the async and await keywords simplify your service code as it avoids the complexities of using callbacks and also provides you much better control over the asynchrony.
Next, you'll need to create a service client project and then add the service reference. In this example we will consider a console application as the service client. The following code illustrates how the service operation can be consumed:
using AsyncWCFClient.DemoServiceReference;
using System;
using System.Collections;
using System.Threading.Tasks;
namespace AsyncWCFClient
{
class Program
{
/// <summary>
/// This is a sample demo to illustrate how Asynchronous Calls work in WCF 4.5.
/// You can use a Data Contract to store data and display the data as shown in this example.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
ArrayList arrList = new ArrayList();
arrList.Add("Joydip");
arrList.Add("Kanjilal");
DisplayText(arrList)
.ContinueWith(delegate
{
Console.WriteLine("Complete...");
}
);
Console.Read();
}
static async Task DisplayText(ArrayList arrList)
{
for (Int32 index = 0; index < arrList.Count; index++)
{
try
{
Task<string> task = new DemoServiceClient().GetStringDataAsync(arrList[index].ToString());
if (task == await Task.WhenAny(task, Task.Delay(3500)))
{
Console.WriteLine(await task);
}
else Console.WriteLine("The execution has timed out...");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
}
Now In Sync
With the introduction of .NET Framework 4.5, the Task-based asynchronous programming model is much simplified through the use of keywords "await" and "async". I hope you enjoyed this exploration of the asynchronous programming model in WCF 4.5. Happy coding!
About the Author
Joydip Kanjilal, a Microsoft MVP in ASP.NET (2007 - 2012), is a speaker and author of eight books and more than 500 articles. He's been working in IT industry for more than 20 years with more than 16 years in .NET and its related technologies. He blogs at https://www.infoworld.com/blog/microsoft-coder/.