Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the main difference between returning a task by await keyword and returning without await keyword?

I'm wrapping AspNet.Identity. But something confuses me about TPL.

First example:

    public virtual async Task<IdentityResult> RemovePasswordAsync(string userId)
    {
        var user = _store.FindByIdAsync(userId).Result;
        if (user == null)
            throw new InstanceNotFoundException("user");

        user.PasswordHash = String.Empty;
        user.SecurityStamp = String.Empty;
        return await UpdateAsync(user);
    }

    public virtual async Task<IdentityResult> UpdateAsync(TUser user)
    {
        await _store.UpdateAsync(user);
        return new IdentityResult();
    }

Second example:

    public virtual Task<IdentityResult> RemovePasswordAsync(string userId)
    {
        var user = _store.FindByIdAsync(userId).Result;
        if (user == null)
            throw new InstanceNotFoundException("user");

        user.PasswordHash = String.Empty;
        user.SecurityStamp = String.Empty;
        return UpdateAsync(user);
    }

    public virtual async Task<IdentityResult> UpdateAsync(TUser user)
    {
        await _store.UpdateAsync(user);
        return new IdentityResult();
    }

And client will call this like:

    result = await _userManager.RemovePasswordAsync(user.Id);

My first question is:

When the client calls the second method, the work is offloaded to a threadpool thread from the IO thread. And when RemovePasswordAsync is called it calls UpdateAsync which has an await keyword. So, at that point does this threadpool thread offload to another threadpool thread? Or does TPL continue using the same thread instead?

And my second question is; what is the main difference between the first implementation and the second implementation of constructing this async method?

EDIT:

This is the update method of the UserStore class. (_store.UpdateAsync(user))

    public Task UpdateAsync(TUser user)
    {
        if (user == null)
            throw new ArgumentNullException("user");

        return _userService.UpdateAsync(user);
    }

And this is the update method of the UserService class

    public Task UpdateAsync(TUser user)
    {
        return Task.Factory.StartNew(() => Update(user));
    }
like image 881
Kemâl Gültekin Avatar asked Feb 25 '14 13:02

Kemâl Gültekin


People also ask

What happens when a method returns a task without awaiting it?

An exception that's raised in a method that returns a Task or Task<TResult> is stored in the returned task. If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call.

What does the await keyword do?

The await operator is used to wait for a Promise . It can only be used inside an async function within regular JavaScript code; however it can be used on its own with JavaScript modules.

Can we use task without async await?

C# Language Async-Await Returning a Task without awaitMethods that perform asynchronous operations don't need to use await if: There is only one asynchronous call inside the method. The asynchronous call is at the end of the method. Catching/handling exception that may happen within the Task is not necessary.

Should I return task or await?

Simply return the Task is enough. "when you hit the await, the control flow is returned to the calling method" -- The control flow might be returned to the calling method, particularly it won't return when the awaited task is already complete.


2 Answers

I'll answer your first question.

You're misunderstanding how async/await works.

An async method will run synchronously at least until it hits the first await statement. When it hits an await, it has two options:

  • If the awaitable (e.g. Task) has already completed, execution carries on the current context (i.e. UI Thread, or ASP.NET request's context).
  • If the awaitable hasn't completed yet, it wraps the rest of the method's body and schedules that to be executed on the current context (i.e. UI Thread) (*) when the task completes.

By this definition, your whole code will run on the same ASP.NET request's context.

_store.UpdateAsync may however spawn a ThreadPool thread (e.g., by using Task.Run).

Updated

According to your updated answer, Update(user) will run on a ThreadPool thread. Everything else will run on the current context.


(*) The rest of the method's body will be scheduled to run on a ThreadPool thread if there's no synchronization context (i.e., Console Application).

like image 79
dcastro Avatar answered Oct 02 '22 14:10

dcastro


And my second question is; what is the main difference between the first implementation and the second implementation of constructing this async method?

Your first implementation can and should be improved by replacing the blocking _store.FindByIdAsync(userId).Result with asynchronous await _store.FindByIdAsync(userId):

public virtual async Task<IdentityResult> RemovePasswordAsync(string userId)
{
    var user = await _store.FindByIdAsync(userId);
    if (user == null)
        throw new InstanceNotFoundException("user");

    user.PasswordHash = String.Empty;
    user.SecurityStamp = String.Empty;
    return await UpdateAsync(user);
}

Without such update, the difference is perhaps described best by Eric Lippert here. One particular thing is how exceptions can possibly be thrown and handled.

Updated to address the comments. You should not be offloading with Task.Factory.StartNew or Task.Run in ASP.NET. This is not a UI app where you need to keep the UI responsive. All this does is just adding an overhead of a thread switch. The HTTP request handler which calls Task.Run then awaits or blocks will take at least the same number of threads to complete, you will not improve scalability and only hurt the performance. On the other hand, it does make sense to use naturally IO-bound tasks, which don't use a thread while pending.

like image 36
noseratio Avatar answered Oct 02 '22 14:10

noseratio