检查文件是不是存在异步?
Posted
技术标签:
【中文标题】检查文件是不是存在异步?【英文标题】:Check if a file Exists async?检查文件是否存在异步? 【发布时间】:2013-10-05 06:42:13 【问题描述】:我希望有一个File.ExistsAsync()
我有:
bool exists = await Task.Run(() => File.Exists(fileName));
为此使用线程感觉像是一种反模式。 有没有更清洁的方法?
【问题讨论】:
我能问一下为什么需要一个线程吗? 嗯,我不想要一个线程,但我也不想阻塞。 File.Exists 真的会阻塞吗? 使用File.Exists()
是一种自动竞争条件。它可以返回true
,同时另一个线程刚刚删除了它。只是说。
@MatthewWatson 同步版本还引入了竞态条件;另一个线程(或者更有可能是另一个进程)可能刚刚删除了它。尽管如此,File.Exists
可能需要几分钟才能完成(例如,机器不存在、连接可能很慢或无法读取磁盘),因此使用 I/O 完成端口的File.ExistsAsync
是最合适的欢迎。不幸的是,内部GetFileAttributesEx
调用似乎不能重叠,所以没有什么意义。
【参考方案1】:
没有 File.ExistsAsync 可能是有充分理由的;因为拥有一个绝对没有意义; File.Exists 不会花费很长时间;我测试了文件存在时为0.006255ms,文件不存在时为0.010925ms。
有几次调用 File.Exists 是明智的;但是通常我认为正确的解决方案是打开文件(从而防止删除),捕获任何异常 - 因为不能保证在调用 File.Exists 后文件将继续存在。
当你想创建一个新文件而不覆盖旧文件时:
File.Open("fn", FileMode.CreateNew)
对于大多数用例,我认为 File.Open()(无论是现有的还是创建新的)会更好,因为一旦调用成功,您将拥有文件句柄并能够做某事用它。即使使用文件的存在作为标志,我想我仍然会打开和关闭它。我真正使用 File.Exists 的唯一一次是在调用浏览器之前检查本地 html 文件是否存在,这样当它不存在时我可以显示一个很好的错误消息。
不能保证在 File.Exists 之后其他东西不会删除文件;因此,如果您在检查 File.Exists 后确实打开了它,则打开调用仍然可能失败。
在我的测试中,在网络驱动器上使用 FileExists 需要比 File.Open 更长的时间,File.Exists 需要 1.5967 毫秒,而 File.OpenRead 需要 0.3927 毫秒)
也许如果您能详细说明您这样做的原因,我们将能够更好地回答;在那之前我会说你不应该这样做
【讨论】:
如果文件在网络驱动器上怎么办?那么即使是像查找文件是否存在这样简单的事情也可能需要很长时间。 很好奇 File.Exists 如果文件位于当前处于停止状态的辅助 HDD 上可能需要多长时间,因此需要 5-10 秒才能启动。 AFAIK,默认情况下,Windows 会在 20 分钟左右不活动的情况下停止辅助驱动器,我猜在使用电池供电的笔记本电脑上,即使在主驱动器上也会发生这种情况。 我不得不不同意这一点(是的,我知道,2 年后,但它出现在我正在寻找的东西中 >. @TylerW IMO 这是一个过于僵化的原则,无法在不单独考虑每个案例的情况下适用。 File.Open 仍然可能失败,因此您应该处理异常。在您有顺序执行的单线程、单用户应用程序中,在打开/创建之前调用 File.Exist 几乎是安全的,但是在任何其他情况下,唯一可靠的方法是打开文件(以正确的模式)和正确处理异常。 @RichardHarrison 我同意 Tyler W 的观点。您应该使用 File.Exists 来尝试避免创建异常,当然您不能依赖结果,但异常应该就是这样 - 异常。异常应该是控制代码中逻辑流的次要机制,而不是主要机制。您是否在计时中考虑了抛出和捕获异常所花费的时间?【参考方案2】:没有比您的解决方案更清洁的方法了。
除了竞争条件的问题,我相信您的解决方案可以在某些情况下使用。 例如
我在许多不同的文件夹中有静态文件内容。 (在我的情况下是 cshtml 视图、脚本文件、css 文件,用于 mvc) 这些文件(在应用程序执行期间变化不大)总是在对网络服务器的每个请求中检查,由于我的应用程序体系结构,与默认 mvc 应用程序相比,检查文件的位置要多得多。以至于 file.exists 占用了每个请求相当多的时间。
所以比赛条件一般不会发生。对我来说唯一有趣的问题是性能
使用 Task.Factory.StartNew() 启动任务需要 0.002 毫秒(来源 Why so much difference in performance between Thread and Task?)
调用 file.exists 需要“当文件存在时为 0.006255ms,当文件不存在时为 0.010925ms”。 [理查德·哈里森]
所以通过简单的数学调用异步 File.Exists 需要 0.008 毫秒到 0.012 毫秒
在最好的情况下,异步 File.Exists 的时间是 File.Exists 的 1.2 倍,在最坏的情况下,它需要 1.3 倍的时间。 (在我的情况下,大多数搜索的路径都不存在)所以大多数时候 File.Exists 大多接近 0.01 毫秒
因此开销并不大,您可以更有效地利用多个内核/硬盘控制器等。通过这些计算,您可以看到异步检查是否存在 2 个文件,在最坏的情况下(0.02/ 0.012),您的性能已经提高了 1.6
好吧,我只是异步异步 File.Exists 在特定情况下是值得的。
我的帖子的注意事项: 我可能没有正确计算所有内容 我四舍五入 我没有衡量单台电脑的性能 我从其他帖子中获得了表现 我刚刚添加了 File.Exists 和 Task.Factory.StartNew() 的时间(这可能是错误的) 我忽略了多线程的很多副作用
【讨论】:
【参考方案3】:很久没发这个帖子了,今天终于找到了……
ExistsAsync 绝对应该是一件事。事实上,在 UWP 中,您必须使用 Async 方法来确定文件是否存在,因为它可能需要超过 50ms 的时间(任何“可能”需要超过 50ms 的时间在 UWP 语言中都应该是异步的)。
但是,这不是UWP
。我需要它的原因是检查folder.exists
,如果在网络共享、远程磁盘或空闲磁盘上会阻塞 UI。所以我可以输入所有的消息,比如“正在检查...”,但是如果没有aysnc
(或ViewModel
,或timers
等),UI 不会更新
bool exists = await Task.Run(() => File.Exists(fileName));
完美运行。在我的代码中,我同时拥有(Exists
和 ExistsAsync
),因此我可以在非 UI 线程上运行 Exists()
而不必担心开销。
【讨论】:
我的 C# aspnetcore webapi(图像上传)上的请求过多,我收到 error500(写入错误),在我将上传文件检查放在一个额外的线程中之后(如这里解释的),我没有更多error500 :)以上是关于检查文件是不是存在异步?的主要内容,如果未能解决你的问题,请参考以下文章
Node + Sequelize:如何在添加之前检查项目是不是存在? (异步混淆)