c# - 当您阻塞同步方法的异步包装器时,资源会发生什么情况?
问题描述
我在我们的生产代码库中看到了一些遵循如下模式的奇怪代码。我想知道何时Start()
调用与直接运行同步代码对性能的影响是什么。特别是在这种情况下如何管理任务或线程。
public Task RunLongTask(){
return Task.Run(()=>{
// Run some Synchronous long IO process
}
}
public void Start(){
RunLongTask().Wait();
}
我的想法是否正确,在等待同步 IO 调用完成时会阻止一项任务。并且来电者RunLongTask()
也将被阻止。本质上是直接运行同步代码的成本翻倍。由于双重阻塞,在这种情况下是否总是会创建 2 个线程?
解决方案
Task.Run
ThreadPool
根据MSDN ,方法将指定的工作排入队列以在 上运行。Task.Wait
方法是阻塞一个。可以在这篇文章中找到对此的简单解释
即使底层任务是异步的,如果您在任务上调用阻塞方法或阻塞属性,执行将等待任务完成 - 但会同步执行,这样当前线程在等待期间完全被占用。因此,如果您使用上述属性/方法之一,请确保这实际上是您想要做的。
因此,您当前的线程(执行RunLongTask().Wait();
)将被阻塞,直到其中Task
的工作同步完成
Task.Run(()=>{
// Run some Synchronous long IO process
}
直接运行同步代码不会加倍,您的主线程只是被阻塞并等待直到 I/O 进程在单独的线程上完成
由于双重阻塞,在这种情况下是否总是会创建 2 个线程?
你已经至少有一个Thread
,当前的,它正在调用Task.Run
。Task.Run
将从线程池中获得一个空闲的工作线程(如果有的话),但它将是一个不同的线程。
就性能而言,在单独的线程上运行 I/O 操作效率不高(因为它可能用于 CPU 密集型工作),因为工作线程将主要等待发送/接收数据。最好让你的长 IO 进程异步并在不阻塞的情况下运行它(当然,如果它支持异步 API)。
另一个潜在的性能缺陷是 ASP.NET Core 每个请求使用一个线程,并且运行具有长时间运行 IO 进程的后台线程将减少可用于处理新请求的线程数量(以及应用程序的可伸缩性)
推荐阅读
- python - Pandas:如何仅删除列末尾的最后一个连字符
- google-forms - 如何根据其他用户过去的回复使 Google 表单自动填充?
- c - 我知道一个地址,但我应该如何判断在哪个部分?使用C的全局区域或堆区域或堆栈区域
- json - 如何解决“手势 RangeError(索引)捕获的异常:无效值:有效值范围为空:0”
- c++ - 什么会使多边形算法中的点仅在正向和反向运行时才能正常工作?
- php - PHP:如何使用键->值对每两个分隔符将一个字符串分解为一个数组
- kubernetes - 将 GKE 集群拆除为“全新”状态而不删除它?
- scala - Spark——自定义reduce函数保存到磁盘然后上传到s3
- javascript - XMLHttpRequest 返回事件对象而不是实际数据
- fortran - Fortran 将未知大小的文件读入数组