What is the main difference between returning a task with a wait keyword and returning without a wait keyword?

I am wrapping AspNet.Identity. But something bothers 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 the customer will call it as follows:

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

My first question is:

When the client calls the second method, the work is unloaded into the threadpool stream from the I / O stream. And when RemovePasswordAsync is called, it calls UpdateAsync, which has a wait keyword. So, at this point is this threadpool thread unloaded into another thread thread? Or does TPL continue to use the same stream instead?

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

EDIT:

This is a method to update 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 method of updating the UserService class

  public Task UpdateAsync(TUser user) { return Task.Factory.StartNew(() => Update(user)); } 
+8
multithreading c # task-parallel-library
source share
2 answers

I will answer your first question.

You do not understand how async / wait works.

The async method will execute synchronously, at least until it hits the first await statement. When he gets into await , he has two options:

  • If the expected (e.g. Task ) is already completed, execution wraps the current context (i.e., the user interface thread or ASP.NET request context).
  • If the expected is not yet completed, it completes the rest of the method body and plans what will be executed in the current context (for example, the user interface thread) (*) when the task is completed.

By this definition, all of your code will work in the same ASP.NET request context.

_store.UpdateAsync can, however, spawn a ThreadPool thread (for example, using Task.Run ).

Update

According to your updated answer, Update(user) will work in ThreadPool thread. Everything else will work in the current context.


(*) The rest of the method body will be scheduled to run in the ThreadPool thread if there is no synchronization context (i.e., a console application).

+3
source share

And my second question: what is the main difference between the first implementation and the second implementation of the construction of this asynchronous method?

Your first implementation can and should be improved by replacing the _store.FindByIdAsync(userId).Result 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 an update, the difference is perhaps best described by Eric Lippert here . One thing: how can exceptions be thrown and handled.

Updated for comments . You should not unload using Task.Factory.StartNew or Task.Run in ASP.NET. This is not a user interface application in which you need to maintain responsiveness of the user interface. All this just adds overhead to the flow switch. The HTTP request handler that calls Task.Run , then waits or blocks, will take at least the same number of threads to complete, you will not improve scalability or damage performance. On the other hand, it makes sense to use natural IO-related tasks that do not use the thread in anticipation.

+2
source share

All Articles