如何实现异步 File.Delete/Create/Move?

Posted

技术标签:

【中文标题】如何实现异步 File.Delete/Create/Move?【英文标题】:How to implement an async File.Delete/Create/Move? 【发布时间】:2015-05-14 20:33:14 【问题描述】:

由于我必须在我的应用程序中执行大量文件 I/O 操作, 我决定异步实现它们。 查看 MSDN,File.Create、File.Delete 和 File.Move 没有异步对应项。据我了解,原因是不存在用于文件删除、创建或移动的异步 Win32 实现,所以我最终得到了以下解决方案:

public static Task DeleteAsync(string path)

     Guard.FileExists(path);

     return Task.Run(() => File.Delete(path));


public static Task<FileStream> CreateAsync(string path)

     Guard.IsNotNullOrWhitespace(path);

     return Task.Run(() => File.Create(path));


public static Task MoveAsync(string sourceFileName, string destFileName)

     Guard.FileExists(sourceFileName);
     Guard.IsNotNullOrWhitespace(destFileName);

     return Task.Run(() =>  File.Move(sourceFileName, destFileName); );

考虑到范式"Don’t use Task.Run in Libraries",我想知道是否有更好的实现或者我应该回退到同步代码?

提前非常感谢!

编辑:


根据 Peter Duniho 的建议改进了代码 添加了 Sriram Sakthivel 提供的原始博客文章的链接

【问题讨论】:

谁说“不要在图书馆使用Task.Run()”?您认为如何在不使用同步方法或其他等效方法的情况下执行同步方法?您的实现实际上有什么问题吗? @PeterDuniho 这是 stephen cleary 的推荐。他建议不要在实现中使用Task.Run,如果您需要将同步方法包装为异步操作,则在您需要的客户端代码中进行。编辑:Stephen toub 还说不要在同步方法上公开异步包装器。 顺便说一句,您的实现似乎并不完美。这些方法都应该只返回Task(没有配置等待,也没有方法是async)。 CreateAsync() 可以 return Task.Run(() =&gt; File.Create(path)); (即返回的等待任务本身会返回 FileStream 对象...无需等待自己这样做,也无需使用变量捕获来完成它)。 @SriramSakthivel:Cleary 的意见当然受到尊重,但这只是一种意见。 为什么他不建议这样做,真的应该将其视为绝对建议还是只是一般建议?你有参考吗?如果整个目标是为本来就同步的事物提供异步库,该怎么办?当一个库可以提供相同的东西时,为什么客户端代码必须一遍又一遍地包装相同的东西? @PeterDuniho 他们成功时的操作非常快......失败时他们可能会很慢:)(你知道当你尝试删除文件时,文件正在使用中,并且五个几秒钟后,资源管理器告诉您该文件正在使用中,您讨厌它:-)) 【参考方案1】:

如果你必须这样做,我会编写这样的方法(注意:我很同意这正是 Stephens Cleary 和 Toub 敦促我们不要这样做的):

public static Task DeleteAsync(string path)

     Guard.FileExists(path);

     return Task.Run(() =>  File.Delete(path); );


public static Task<FileStream> CreateAsync(string path)

     Guard.IsNotNullOrWhitespace(path);

     return Task.Run(() => File.Create(path));


public static Task MoveAsync(string sourceFileName, string destFileName)

     Guard.FileExists(sourceFileName);
     Guard.IsNotNullOrWhitespace(destFileName);

     return Task.Run(() =>  File.Move(sourceFileName, destFileName); );

这会稍微清理代码并消除过多的上下文/线程切换。

在基于 GUI 的程序的上下文中,使用这样的包装器似乎很好。我认为,只要您不创建一个具有并行同步和异步 API 的全新库,如所引用的文章中所述,这并不可怕。

但对我来说,更大的问题是,这些操作都不太可能花费足够长的时间来证明首先使它们异步是合理的。 IE。您从 UI 线程在 Task 中运行的通常原因是您的 UI 线程在操作完成时无法等待。但是在这里,对于这些操作中的每一个,将操作发送到线程池,然后在完成后继续执行的行为可能会给您的程序增加与操作本身一样多的性能开销。

出于那个的原因,我建议完全不要使用异步版本的方法。只需直接从 UI 调用 Create()Delete()Move() 方法即可。

(注意:上面的一个例外是如果处理网络共享或不同的卷,其中Move() 涉及实际复制数据。所以即使在那里,它也是一个巨大的“取决于”。同样,虽然@987654327 @ 和 Create() 通常即使在网络上也很快,如果操作实际上会失败,它们可能需要一段时间。您实际上可能有一个很好的用例来在那里异步运行操作)。

【讨论】:

作为旁注,同步使用Guard.FileExists(sourceFileName) 可能是错误的。如果sourceFileName 在另一台计算机上,回复速度很慢,那么您已经返回到点 0 :-) @xanatos:是的,我也同意这个观点。如果没有看到实现,就不可能确定,但​​假设它只是委托给File.Exists() 并在返回false 时抛出异常似乎是安全的。 @Peter Duniho:在 WinRT 中,所有文件操作都是异步的,所以我想知道这对常见的 .NET 应用程序是否也有好处。好的 File.Create 的异步实现是值得怀疑的,但删除和移动可以长时间运行,即使操作成功。您认为仅实现 Move async 和 Create/Delete 同步是一个好方法吗? @Peter Duniho:是的 File.Exits 完全按照您的描述实现。我忘了把它放在我的示例代码之外。 @Fabe:WinRT 是一个稍微不同的野兽,因为它的 API 专门抽象了云,并期望使用云是标准场景。这是一件好事,但它使通常认为非常快的文件操作很可能很慢。在任何情况下,即使在桌面上,取决于场景,您可能对所有这三个操作都有延迟,因此您可能毕竟有理由使用异步包装器.

以上是关于如何实现异步 File.Delete/Create/Move?的主要内容,如果未能解决你的问题,请参考以下文章

如何在一个类中实现异步

如何实现支持异步方法的类?

如何实现异步执行任务

如何实现异步执行任务

AngularJS如何实现异步

如何使用 Apollo Gateway 实现异步突变