1

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??!

0

3 Answers 3

8

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.

Sign up to request clarification or add additional context in comments.

Comments

3

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/

4 Comments

Sure, there are plenty of different ways to start the Task. They might have a slightly different effect depending on what you do exactly but in such a trivial example as here it should be fine either way. Most important aspect is actually starting the task.
Doh! Thanks. I've misunderstood await, I thought it also ensured the task ran.
@Chris: you should accept Stephen's answer instead of mine, it's way better.
@JeroenVannevel duly done, I agree it's a better answer but you solved my initial dumb question, so thank you again.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.