我正在包装AspNet.Identity。但有些事让我对TPL感到困惑。
第一个例子:
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();
}
第二个例子:
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();
}
客户会称之为:
result = await _userManager.RemovePasswordAsync(user.Id);
我的第一个问题是:
当客户端调用第二个方法时,工作将从IO线程卸载到线程池线程。当RemovePasswordAsync
被调用时,它会调用UpdateAsync
,它有一个await关键字。那么,在这一点上,这个线程池线程是否会卸载到另一个线程池线程?或者TPL是否继续使用相同的线程?
我的第二个问题是;构建此async
方法的第一个实现和第二个实现之间的主要区别是什么?
修改
这是UserStore
类的更新方法。 (_store.UpdateAsync(user)
)
public Task UpdateAsync(TUser user)
{
if (user == null)
throw new ArgumentNullException("user");
return _userService.UpdateAsync(user);
}
这是UserService
类
public Task UpdateAsync(TUser user)
{
return Task.Factory.StartNew(() => Update(user));
}
答案 0 :(得分:3)
我会回答你的第一个问题。
你误解了async / await是如何工作的。
async
方法将同步运行至少,直到它遇到第一个await
语句。
当它到达await
时,它有两个选项:
Task
)已经完成,则执行将继续当前上下文(即UI线程或ASP.NET请求的上下文)。通过这个定义,您的整个代码将在相同的ASP.NET请求的上下文中运行。
然而, _store.UpdateAsync
可能会产生一个ThreadPool线程(例如,使用Task.Run
)。
<强>更新强>
根据您更新的答案,Update(user)
将在ThreadPool线程上运行。其他所有内容都将在当前环境中运行。
(*)方法主体的其余部分将被安排在ThreadPool线程上运行,如果没有同步上下文(即控制台应用程序)。
答案 1 :(得分:2)
我的第二个问题是;两者之间的主要区别是什么 第一次实施和第二次实施 这个异步方法?
通过使用异步_store.FindByIdAsync(userId).Result
替换阻止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);
}
如果没有这样的更新,Eric Lippert here可能最好地描述了这种差异。一个特别的事情是how exceptions can possibly be thrown并处理。
已更新以解决评论。您应该不在ASP.NET中使用Task.Factory.StartNew
或Task.Run
进行卸载。这是不您需要保持UI响应的UI应用。所有这一切只是增加了线程切换的开销。调用Task.Run
然后等待或阻塞的HTTP请求处理程序将至少完成相同数量的线程,您不会提高可伸缩性并且只会损害性能。另一方面,使用自然的IO绑定任务是有意义的,这些任务在挂起时不使用线程。