异步接口,只有同步实现:在Task.FromResult与将异步封装为同步调用之间,哪种更小的祸害?
我有一个接口,用来从不同提供者访问文件:
interface IFileProvider
{
Task<bool> FileExistsAsync(string filePath);
...
}
它的一些实现使用支持异步调用的网络请求,但有一个例外:基础的文件系统实现,使用 File.Exists()。
就性能和死锁风险而言,哪种做法相对更优?在调用线程上直接执行阻塞调用:
class FileSystemImplementation : IFileProvider
{
Task<bool> FileExistsAsync(string filePath) => Task.FromResult(File.Exists(filePath));
}
还是做成“异步覆盖同步”的模式?
class FileSystemImplementation : IFileProvider
{
Task<bool> FileExistsAsync(string filePath) => Task.Run(() => File.Exists(filePath));
}
甚至
class FileSystemImplementation : IFileProvider
{
async Task<bool> FileExistsAsync(string filePath) => await Task.Run(() => File.Exists(filePath));
}
?
解决方案
我会从第一种选项开始,即 Task.FromResult(File.Exists(..))。原因是在本地文件系统上检查文件是否存在通常既快速又可预测。
一些快速测试表明,对我而言这通常少于100微秒,但这高度取决于磁盘延迟、缓存等因素。
如果这成了问题,例如磁盘更慢、网络驱动器等原因,你总是可以改用“异步覆盖同步”模式。我也建议你自行进行测量,看看线程被阻塞了多久,并思考你的具体使用场景能容忍多长的延迟。
对于UI应用,经验法则是不让UI线程被阻塞超过大约100毫秒,而我测得的时间远低于这个阈值。
使用 Task.FromResult 将返回一个已经完成的任务。这应该不会引发死锁或其他错误,因为你不应该对任务何时完成做出任何假设。如果它确实会引发问题,你只是让现有的一个bug更容易复现。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。