Chris
Chris

Reputation: 3221

Async controller method not returning

This code hangs (does not return a response) when I make a request to it:

public class MyController : ApiController
{
    public async Task<IQueryable<int>> Get()
    {
        return await new Task<IQueryable<int>>(() => new List<int>().AsQueryable());
    }
}

But this method works fine:

public IQueryable<int> Get()
{
    return new List<int>().AsQueryable();
}

What fundamental knowledge am I missing??!

Upvotes: 1

Views: 1431

Answers (3)

illegal-immigrant
illegal-immigrant

Reputation: 8224

There is absolutely no need in async/await there, the method can look like:

public Task<IQueryable<int>> Get()
{
    return Task.FromResult(new List<int>().AsQueryable());
}

If you really need it to be async, ok, you can always write something like:

public async Task<IQueryable<int>> Get()
{
    return await Task.FromResult(new List<int>().AsQueryable());
}

which will introduce little overhead (a whole state machine will be generated by compiler).

Also, as others already stated, tasks returned from async methods should be hot (started)

Keep in mind, that Task.FromResult will return completed task and this case can be optimized by async/await generated code, writing Task.Run in this case is at least wierd

Read Task-based Asynchronous Pattern for more details

Upvotes: 0

Stephen Cleary
Stephen Cleary

Reputation: 456417

As the other answer noted, the reason your controller is not finishing is because the task is not started. However, using Task.Start, Task.Factory.StartNew, or Task.Run is not a good solution on ASP.NET.

The entire point of using async/await on ASP.NET is to free up a thread. However, if you await a task that is started via Start/StartNew/Run, then you're freeing up one thread by taking up another thread from the same thread pool. In this case, you not only lose all the benefits of async/await, but you actually decrease your scalability by regularly throwing off the ASP.NET thread pool heuristics.

There are two types of tasks, as I describe on my blog: Delegate Tasks (which represent some work executed on a thread) and Promise Tasks (which represent an event). You should avoid Delegate Tasks on ASP.NET, including any tasks that are "started" (Start/StartNew/Run).

Since you're returning an IQueryable<T>, I'm assuming that your actual underlying operation is a database query. If you're using EF6, then you have full support for asynchronous queries, which are properly implemented with Promise Tasks.

Upvotes: 8

Jeroen Vannevel
Jeroen Vannevel

Reputation: 44439

You're not actually starting your Task so it will wait for something that will never begin.

Instead use Task.Factory.StartNew which will create and start at the same time, or call Task#Start and await that call.

An overview of ways to start a task: http://dotnetcodr.com/2014/01/01/5-ways-to-start-a-task-in-net-c/

Upvotes: 3

Related Questions