C# 使用线程池和Task.Wait在异步/等待方法中
我刚刚遇到了这个代码。我立刻开始畏缩,自言自语(不是好事)。问题是我真的不明白为什么,也不能合理地表达出来。我只是觉得很糟糕——也许我错了C# 使用线程池和Task.Wait在异步/等待方法中,c#,.net,async-await,task-parallel-library,C#,.net,Async Await,Task Parallel Library,我刚刚遇到了这个代码。我立刻开始畏缩,自言自语(不是好事)。问题是我真的不明白为什么,也不能合理地表达出来。我只是觉得很糟糕——也许我错了 public async Task<IHttpActionResult> ProcessAsync() { var userName = Username.LogonName(User.Identity.Name); var user = await _user.GetUserAsync(userName);
public async Task<IHttpActionResult> ProcessAsync()
{
var userName = Username.LogonName(User.Identity.Name);
var user = await _user.GetUserAsync(userName);
ThreadPool.QueueUserWorkItem((arg) =>
{
Task.Run(() => _billing.ProcessAsync(user)).Wait();
});
return Ok();
}
公共异步任务ProcessAsync()
{
var userName=userName.LogonName(User.Identity.Name);
var user=await\u user.GetUserAsync(用户名);
ThreadPool.QueueUserWorkItem((arg)=>
{
Task.Run(()=>_billing.ProcessAsync(用户)).Wait();
});
返回Ok();
}
在我看来,这段代码似乎不必要地使用ThreadPool.QueueUserWorkItem
和Task.Run
创建线程。另外,它看起来有可能在重载时死锁或造成严重的资源问题。我说得对吗
_billing.ProcessAsync()方法是可等待的(async),因此我认为一个简单的“await”关键字将是正确的选择,而不是所有其他的包袱。这很奇怪,但根据作者试图实现的目标,从异步方法内部将工作项排入线程池似乎是可以的 这并不是启动一个线程,它只是在线程池的线程中有空闲线程时将要执行的操作排队。因此,异步方法(
ProcessAsync
)可以继续,不需要关心结果
奇怪的是lambda中的代码要排队到线程池中。不仅是Task.Run()
(这是超级函数,只会导致不必要的开销),而且在线程池应该运行的方法中调用async
方法是不好的,因为它在等待
等待某些内容时会将控制流返回给调用方
因此,线程池最终认为该方法已完成(队列中的下一个操作没有线程),而实际上该方法希望稍后恢复
这可能导致非常未定义的行为。这段代码可能一直在工作(在某些情况下),但我不会依赖它,而将其用作生产代码
(在Task.Run()
中调用一个未等待的异步方法也是如此,因为Task
“认为”它已经完成,而该方法实际上希望稍后恢复)
作为解决方案,我建议简单地
wait
也使用async
方法:
await _billing.ProcessAsync(user);
当然,如果不知道代码片段的上下文,我不能保证任何事情注意这将改变行为:虽然到目前为止,代码没有等待\u billing.ProcessAsync()
到finsih,但现在可以了。所以,也许不必等待,只需开火然后忘记
_billing.ProcessAsync(user);
也许也足够好。我相信Scott的猜测是正确的,
ThreadPool.QueueUserWorkItem
应该是HostingEnvironment.QueueBackgroundWorkItem
。然而,调用Task.Run
和Wait
是完全没有意义的-它们将工作推送到线程池并在线程池上阻塞线程池线程,而代码已经在线程池上
_billing.ProcessAsync()方法是可等待的(async),因此我认为一个简单的“wait”关键字是正确的选择,而不是所有其他的包袱
我非常同意
但是,这将改变操作的行为。它现在将等待Billing.ProcessAsync完成,而在此之前它将提前返回。请注意,在ASP.NET上提前返回几乎总是一个错误——我想说,任何“计费”处理都更肯定是一个错误。因此,用
await
替换这个混乱将使应用程序更加正确,但会导致ProcessAsync
操作需要更长的时间才能返回到客户端。是的,这非常糟糕。此外,这看起来像是在网页中运行的代码,就像他们执行的ThreadPool.QueueUserWorkItem
一样,实际上是要执行的,如果您在IIS上有后台线程,这就是您应该执行的操作。非常好的响应。我认为简单的“等待”将是推荐的治疗方法。你同意吗?@bigdady是的,忘了结论。。。据我所知,wait
是正确的。如果QueueUserWorkItem中包装的任务是一个长期运行的操作,它会改变什么吗?@bigdady在不知道整个项目的情况下仍然很难说。由于答案中所述的原因,代码被破坏。使用QueueUserWorkItem
,Task.Run()
,或者简单地调用async
方法(带或不带await
)会导致该方法在线程池线程中执行。如果它以前是通过Task.Factory.StartNew(...LongRunning)
调用的,那么它将在一个专用线程中执行,而现在它阻止了一个线程池线程。谢谢,但我有点困惑。正在创建的任务正在使用“等待”,但您说它将提前返回。在这种情况下,“等待”在该任务上实际做了什么?它与“Result”类似吗?@bigdady:Wait
只是在任务完成之前阻塞线程池线程。在这种情况下,它除了使用线程池线程之外没有任何用处-您可以完全摆脱等待
(和任务.Run
),代码的行为将完全相同(除了使用更少的资源)Wait
与Result
非常相似,它们都会阻塞调用线程,直到任务完成。该操作提前返回,因为它没有等待QUWI排队的工作。如果QueueUserWorkItem中包装的任务是一个长期运行的操作,它会改变什么吗?@Bigdady:不会。无论需要多长时间,QUWI在ASP.NET上本质上是不安全的。(其中“u”