Building Task Based WCF Services with Task Parallel Library

Tasks and the Task Parallel Library (TPL) will soon be entering the Windows Communication Foundation (WCF) vernacular. Upcoming .NET language features will be replacing the outdated Asynchronous Programming Model (APM). WCF 4.5 will include Task based options. However, a WCF developer needn't wait for .NET 4.5 to leverage TPL. TPL Tasks shipped with .NET 4.0 and WCF 4.0 works with TPL. There are some tricks to squeezing out all potential performance and avoiding concurrency problems. Building a TPL Task based WCF Service is demonstrated in the paragraphs that follow.

Tasks and the .NET Future

Realizing that CPUs were becoming more numerous and concurrency was going to become more important; Microsoft built TPL to make concurrency easier. TPL helps a developer partition and execute an Application workload on multiple CPUs. An Application workload is partitioned into Tasks classes. The Task class is the TPL core.

Unquestionably Tasks will become more important in future .NET versions. As stated earlier, Tasks will begin to play a larger role in the next versions of the .NET Framework. A new Async language feature is built on Tasks. A new asynchronous programming model centered on Tasks will become part of everything in the .NET Framework.

Typically, a Task is not something that executes for too long nor should it be a single line of code. There are two Task types: a generic Task that returns a Result and a Task that returns no result. Since code can fault, Tasks include Exception handling support. Tasks also support operation cancellation. Some Properties and Methods on the Task class appear below.

    public class Task : IAsyncResult, IDisposable
    {
        public Task(Action action);
        public Task(Action<object> action, object state);
        public Task(Action action, CancellationToken cancellationToken);
        public object AsyncState { get; }
        public TaskCreationOptions CreationOptions { get; }
        public static int? CurrentId { get; }
        public AggregateException Exception { get; }
        public int Id { get; }
        public bool IsCompleted { get; }
        public bool IsFaulted { get; }
        public TaskStatus Status { get; }
        public Task ContinueWith(Action<Task> continuationAction);
 
        public void Start();
        public void Wait();
        public void Wait(CancellationToken cancellationToken);
        public bool Wait(int millisecondsTimeout);
 
        public static void WaitAll(params Task[] tasks);
 

Tasks have other nice features. Developers can block and wait for multiple Tasks to complete before proceeding. Demonstrated later in the article are Continuations. Continuations execute a Task contingent on completing an "antecedent" Task.

Getting TPL working properly with WCF requires understanding WCF Concurrency. WCF Concurrency is housed in the ServiceBehaviors attribute.

WCF ServiceBehaviors

One of the early decisions a WCF developer makes is what to configure on a Service's ServiceBehavior attribute. Of particular interest is what to set Concurrency and Instancing levels to. Does a Service spin-up a new Service class instance each time a service is activated or should a Service use a single instance? WCF Service Instancing is mostly controlled by the ServiceBehavior InstanceContextMode and ConcurrencyMode. The ServiceBehavior configuration from the sample application appears below.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple)]
public class TestServiceService : Test.WCFTPL.Server.ITestServiceContract
{
    public TestServiceService()
    {
        //Change InstanceContextMode to PerCall and this gets called multiple times
        Console.WriteLine("Called TestService Constructor");
    }
 
}
 

While a developer could use Tasks regardless of the ServiceBehavior settings it's important to understand ServiceBehavior, Instancing, and Concurrency dangers. One fatal Concurrency mistake developers make is improperly sharing the same piece of memory. The ServiceBehavior settings above push more instance handling into the Service Operations. Local variables almost eliminate improper sharing. Since only one instance of the Service exists a developer is forced to do more allocation locally in the Operation. For example: Choosing PerSession Instancing leaves open the temptation to share member variables inside a class Instance. Since many WCF Services have more than one operation; that means two Operations running concurrently against the same Session Instance may be unknowingly sharing a space in memory.

Properly configured ServicesBehaviors and Service architectural decisions are important to building a Task Based WCF Service. Full Task based Service benefits will not be realized unless a developer implements the WCF Asynchronous model.

Asynchronous WCF with Tasks

Typically, a WCF Service with Asynchronous Operations will be more per formant than the same Service doing Synchronous Operations. Asynchronous, when done properly, will typically tie up less IO and respond better to a workload. Unfortunately, the Asynchronous Programming Model (APM) WCF follows is difficult to follow. Most developers opt out of Asynchronous unless Asynchronous is the only route.

The Service interface implementation from the sample appears below.

[ServiceContract(Namespace = "Test.WCFTPL.Service")]
interface ITestServiceContract
{
    //Callback and state must be the last two.  REMEMBER parameters must match with the client interface
 
    [OperationContractAttribute(AsyncPattern = true, Action = "TestServiceMethod", Name = "TestServiceMethod", ReplyAction = "TestServiceMethodReply")]
    IAsyncResult BeginTestServiceMethod(RequestObj req, AsyncCallback callback, object asyncState);
 
    // Note: There is no OperationContractAttribute for the end method.
    ResponseObj EndTestServiceMethod(IAsyncResult result);
 
}
 

Only the Begin portion needs the OperationContract Attribute. The following code demonstrates doing Asynchronous with Tasks.

    #region ITestServiceContract Members
 
    public IAsyncResult BeginTestServiceMethod(RequestObj req, AsyncCallback callback, object asyncState)
    {
           
        var task = new Task<ResponseObj>((state) =>
            {
                SpinWait.SpinUntil(() => { return false; }, 1000);
 
                return new ResponseObj() { PayloadOriginal = req.Payload, PayloadResponse = "Response was " + Guid.NewGuid().ToString() };
            }
        ,asyncState);
 
        //When the task completes notify the callback
        task.ContinueWith((t) => { callback(t); });
        task.Start();
 
        return task;
    }
 
    public ResponseObj EndTestServiceMethod(IAsyncResult result)
    {
        var task = (Task<ResponseObj>)result;
 
        return task.Result;
    }
 
    #endregion
 

Admittedly this is still more complicated than the synchronous model. However, the Task implementation is more self-contained, a bit easier to follow, and a little less clumsy.

The Task delegate is contained within a Closure. Task.Start does not immediately execute the Task. Instead, Start queues the Task to run in the .NET Thread Pool. Task implements the IAsyncResult interface. IAsyncResult is the core of the Asynchronous Programming Model. Creating the task with the object state parameter tucks the State away inside the Task class until it is needed again for the "End" portion in the operation.

Continuations are the key to making Asynchronous easier. Continuations are invoked when the Task they're attached to completes. A complete review of Continuation options is beyond the scope of this article, but you'll find more under the resource sections. When the task completes, the Continuation Task is scheduled to run. The Continuation invokes the callback and the WCF infrastructure, in turn, invokes the End Method.

End method unpacks the Result property from the completed Task and returns the result. WCF takes over from there, pushing the serialized class out of the Service operation.

Conclusion

Asynchronous WCF code is more per formant than synchronous code. Tasks and the Task Parallel Library can make handling Asynchronous code easier. However, before using Tasks with WCF a developer should understand how WCF configurations can impact a Task based solution.

Resources

"Sessions, Instancing, and Concurrency"

"What's new in .NET 4.5 for Parallelism"

"Understanding Tasks in .NET Framework 4.0 Task Parallel Library"

Acknowledgements

Special thanks to Stephen Toub with Microsoft.



About the Author

Jeffrey Juday

Jeff is a software developer specializing in enterprise application integration solutions utilizing BizTalk, SharePoint, WCF, WF, and SQL Server. Jeff has been developing software with Microsoft tools for more than 15 years in a variety of industries including: military, manufacturing, financial services, management consulting, and computer security. Jeff is a Microsoft BizTalk MVP. Jeff spends his spare time with his wife Sherrill and daughter Alexandra.

Related Articles

Comments

  • What happens with the exception in Task?

    Posted by Randy Forbes on 03/30/2012 11:17am

    Jeff - Good article and similar to what I'm currently working on. I'm curious how you are handling exceptions that occur within Task since Wait is never called.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 14, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Data protection has long been considered "overhead" by many organizations in the past, many chalking it up to an insurance policy or an extended warranty you may never use. The realities of today make data protection a must-have, as we live in a data driven society. The digital assets we create, share, and collaborate with others on must be managed and protected for many purposes. Check out this upcoming eSeminar and join eVault Chief Technology …

  • Where the business performance of their mobile app portfolios are concerned, most companies are flying blind. While traditional application portfolios are held to all kinds of ROI measure, the investment plan for mobile apps -- increasingly the more crucial bet -- is made by guesswork and dart-throwing. This interactive e-book investigates how mobile is driving the need for app and portfolio measures unlike any we saw in the days of web. Good mobile analytics must deliver leading indicators of user experience …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds