异步打开读取文件

10

我正在使用以下的C#代码来读取一个小文本文件,这个文件存储在网络共享中:

string fileContent;
using (var stream = File.OpenRead(filePath))
using (var reader = new StreamReader(stream, FileEncoding))
{
    fileContent = await reader.ReadToEndAsync();
}

尽管这个文本文件非常小(不到10KB),但有时这个操作需要大约7秒钟才能运行。当发生这种情况时,我注意到大部分时间都花在了

File.OpenRead(filePath)

可能是因为Windows需要解析文件共享并通过网络获取文件锁。由于该方法调用不是异步的,这会阻塞我的当前线程数秒钟。

是否有安全的方式可以异步从磁盘读取文件,并执行OpenRead异步操作?


你尝试过先将文件复制或移动到本地,然后再读取吗?如果还没有尝试过,请尝试一下以进行测试,这可能会显示网络中的其他可能问题。 - Oğuz Sezer
2个回答

18
很遗憾,Win32 API 中没有这个功能。设备驱动程序确实有“异步打开”的概念,因此基础设施已经存在。但是,Win32 API 没有公开此功能。 自然地,这意味着.NET 也无法公开该功能。您可以使用“伪异步”操作 - 即,在 Task.Run 中包装文件读取,以便您的 UI 线程不被阻塞。但是,如果您在 ASP.NET 上,则不要使用 Task.Run;只需保持(阻止)文件打开即可。

2
天啊,能不能把这个添加到Win 10 API中? - Aron
我不使用ASP.NET,但我很想知道为什么在那里使用它会是一个糟糕的选择。有任何参考资料吗? - roim
@aron:据我所知不行,可以打开UserVoice问题。我相信异步关闭也是可能的,但可能更难建模。 - Stephen Cleary
@roim:在客户端UI上使用async的主要目标是释放UI线程,使应用程序更具响应性;使用Task.Run也是可以接受的(但不理想),它只会阻塞线程池线程,因此不会阻塞UI线程。 - Stephen Cleary
9
在ASP.NET中使用async的主要目标是释放线程池线程,以便服务器可以进行扩展;而使用Task.Run会强制进行上下文切换,使用一个线程池线程来释放另一个线程池线程。这是一种净损失。作者在MSDN文章和博客中有介绍。 - Stephen Cleary
根据您的回答,我提出了一个后续问题,因为“异步”IO方法的文档显示在不指定useAsync参数的情况下打开文件流:https://dev59.com/51oU5IYBdhLWcg3w15ay - Alex Hope O'Connor

-1
async Task<Stream> OpenReadAsync()
{      
    FileStream? s = null;

    await Task.Run(() => {
        s = File.OpenRead(FileName);});

    if (s == null) throw new Exception("Error opening file {FileName}");

    return s;
}

1
感谢您为Stack Overflow社区做出的贡献。这可能是一个正确的答案,但如果您能提供代码的额外解释,让开发人员能够理解您的推理过程,那将非常有用。对于那些对语法不太熟悉或者正在努力理解概念的新开发人员来说,这尤其有帮助。您是否可以友好地编辑您的答案,以便为社区的利益提供更多细节? - undefined
s == null条件永远不会成立,所以这个if语句是多余的。实际上整个OpenReadAsync方法也是多余的,因为它违反了不为同步方法提供异步包装的准则。你可以直接在想要打开文件的位置使用FileStream s = await Task.Run(() => File.OpenRead(fileName)); - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接