4

Consider a time consuming synchronous method "Foo":

public int Foo(int id)
{
    // Do some expensive calculation
    return 42;
}

And a WCF Service hosted in IIS called "FooService" which calls "Foo":

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class FooService 
{
    public Task<int> GetFoo(int id)
    {
        return Task.Factory.StartNew(() => return Foo(id));
    }

    public int GetFoo2(int id)
    {
        return Foo(id);
    }
}

Do I get any benefits if I start and return a Task for Foo? Like - do I reduce load to the I/O Thread?

Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
  • You should not be using `Task.Factory.StartNew` or `Task.Run` to wrap a synchronous, CPU-bound worker method in `ASP.NET`. All this does is just adding an overhead of a thread switch. An HTTP request calling `Task.Factory.StartNew` and returning it as a `Task` will take at least the same number of threads to complete, you will not improve scalability and only decrease the performance. OTOH, it does make sense for naturally IO-bound tasks, which don't use a thread while pending. – noseratio Feb 25 '14 at 00:35

3 Answers3

4

Tasks in WCF provide a more convenient API than that of the APM pattern (BeginXX/EndXX). Why should you use asynchronous calls in WCF? Because, when done correctly, it would lead to a much better thread utilization, which in turn, allows your application to be more scalable.

On the client side, having a task-based service contract, makes it easier to call a service without blocking a thread, waiting for the call to return. Even if the operation is a synchronous, CPU-bound operation on the server side, the client side would benefit from having a task. Also, with tasks, it's very simple to make the call synchronous again by calling Task.Wait() or Task<T>.Result.

On the service side, a task-based operation can be useful in several scenarios:

  • You want to parallelize some CPU-bound actions
  • You're doing IO (e.g. calling another service, reading a file, etc.)
  • Any combination of the above

Every time a WCF operation is called, WCF grabs a thread from the thread-pool to handle the request. So there's no need to call StartNew, which queues an operation to the thread-pool (a completely redundant overhead):

public Task<int> GetFoo(int id)
{
    return Task.Factory.StartNew(() => return Foo(id));
}

Instead, you can use FromResult, which creates a completed task object:

public Task<int> GetFoo(int id)
{
    return Task.FromResult(new Foo(id));
}

Lastly, if none of the above use cases are relevant, and your client API needs to be synchronous, there's no point in using tasks.

Eli Arbel
  • 22,391
  • 3
  • 45
  • 71
  • Thanks Eli. Most of the methods do some time consuming database queries so I think it is a good idea to return a Task. I thought using Task.Factory.StartNew will allow me to run more concurrents requests - while Task.FromResult won't? – Martin Brandl Feb 24 '14 at 14:10
2

In my opinion, no, you won't get any benefit from spinning off a Task thread to do the work as described in your post. The reason is this: each WCF web service defined using WSHttpBinding or BasicHTTPBinding (can't speak to the others) will create its own independent thread for each incoming request, so I don't see the advantage. So if 10 requests coming at 12:00:01:00, each will get it's own thread from WCF automatically.

Now, if within each singular request you want threading to speed up some repetitive, time-consuming tasks, then you'd obviously benefit from setting up and managing threads through Task, but that's about it.

Brian
  • 3,653
  • 1
  • 22
  • 33
1

Please define a time consuming synchronous method

Overhead of asynchronous operations is not negligible. You should only use task if you are doing an IO-bound operation (calling another web service/API, reading a file, reading a lot of data from database or running a slow query).

Using async programming on server-side differs from client-side. We are not concerned by UI thread but by scalability. Using async programming on server-side will allow you to run thousands of concurrents requests.

This is very important since IIS 7, as explained here

... in IIS 7.0 integrated mode, ASP.NET restricts the number of concurrently executing requests. The difference only matters when the requests are asynchronous (the request either has an asynchronous handler or a module in the pipeline completes asynchronously). Obviously if the reqeusts are synchronous, then the number of concurrently executing requests is the same as the number of threads concurrently executing requests, but if the requests are asynchronous then these two numbers can be quite different as you could have far more requests than threads.

Note : since .net 4.5, it's better to use Task.Run.

Community
  • 1
  • 1
Cybermaxs
  • 24,378
  • 8
  • 83
  • 112
  • Thanks a lot for your answer. Most of the methods do some database queries which can take some time (1 - 5 seconds). So if I return a Task here, my Service can handle more concurrent requests? Please note the lack of the async keyword. – Martin Brandl Feb 24 '14 at 13:16
  • Thanks also for the Task.Run hint – Martin Brandl Feb 24 '14 at 13:19
  • @jisaak yes, and especially if you follow the asynchronous pattern all the way – Cybermaxs Feb 24 '14 at 13:50