【问题标题】:async/await and opening a FileStream?异步/等待并打开 FileStream?
【发布时间】:2016-08-30 17:23:32
【问题描述】:

在尝试确定我是否正确使用了Stream 方法(例如ReadAsync 和CopyToAsync)时遇到了以下问题: C# 4.5 file read performance sync vs async

在这个问题中,我在接受的答案中阅读了以下内容:

最值得注意的是,您的“异步”测试不使用异步 I/O;带文件 流,您必须将它们显式地以异步方式打开,否则 您只是在后台线程上执行同步操作。

在他的异步 IO 代码中,他使用以下代码“异步”打开 FileStream:

var file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true)

所以我想知道您是否打算使用诸如CopyToAsync 之类的方法,是否应该打开底层的FileStream,如上所示?而不是像下面这样简单:

File.Open(filename, FileMode.Open)

CopyToAsync 的实际文档中的示例演示如何打开底层FileStream: https://msdn.microsoft.com/en-us/library/hh159084(v=vs.110).aspx

如果底层FileStream打开方式无关紧要,FileStream构造函数的useAsync参数是做什么的?

【问题讨论】:

    标签: c# .net async-await .net-4.5


    【解决方案1】:

    The MSDN website 说:

    useAsync

    输入:System.Boolean

    指定是使用异步 I/O 还是同步 I/O。 但是请注意,底层操作系统可能不支持 异步 I/O,因此在指定 true 时,可能会打开句柄 同步取决于平台。异步打开时, BeginRead 和 BeginWrite 方法在大型 读取或写入,但对于小型读取或写入可能会慢得多 写道。如果应用程序旨在利用 异步 I/O,将useAsync 参数设置为true。使用 正确的异步 I/O 可以将应用程序加速多达 因子 10,但使用它而不重新设计应用程序 异步 I/O 可以将性能降低多达 10.

    【讨论】:

    • 所以其他异步IO方法比如CopyToAsync也无所谓了?
    • 是的,对于其他异步 IO 方法(如 CopyToAsync)来说很重要。但不幸的是,MSDN 的说法并不重要。
    • 我也试过了,结果是在 Win 7 x64 SP2 上,总是冻结在 80-99%,但有一个例外,例如:“无法从流中读取更多字节”。关闭useAsync 标志后,一切都像魅力一样,不再冻结。所以似乎连 MS 都不知道那个标志是做什么的。 ;)
    【解决方案2】:

    所以我想知道您是否打算使用诸如 CopyToAsync 之类的方法,是否应该像上面所示打开底层 FileStream,而不是像 File.Open 这样简单的事情?

    我用ILSpy反编译查看File.Open。

    public static FileStream Open(string path, FileMode mode)
    {
        return File.Open(path, 
                         mode, 
                         (mode == FileMode.Append) 
                             ? FileAccess.Write 
                             : FileAccess.ReadWrite, 
                         FileShare.None);
    }
    

    这叫:

    public static FileStream Open(string path, FileMode mode, FileAccess access, FileShare share)
    {
        return new FileStream(path, mode, access, share);
    }
    

    这个特定的FileStream 构造函数传入false 用于useAsync 参数。所以,是的,这似乎很重要。但是,您仍然可以调用 async API,它仍然可以按您的预期工作。

    正如汉斯·帕桑特所说:

    基础CreateFile() 调用然后使用FILE_FLAG_OVERLAPPED 选项。这启用了重叠 I/O,这是一种在 winapi 级别启用异步读写的机制。

    FileStream 类有一个 _isAsync 布尔值,表示“如果此平台不支持异步 IO,或者此 FileStream 未使用 FileOptions.Asynchronous 打开。”。

    同样,您仍然会得到一个Task,它代表您想要的异步操作。

    【讨论】:

    • you can actually look at the source 时为什么要使用反编译器?
    • 参考源很棒,我一直都在使用它,但有什么保证它是迄今为止的更新?我实际上不知道......无论哪种方式都会导致答案。
    • 它说它显示了 .Net Framework 4.6.1 的源代码,应该是最新的。
    • 所以如果我理解的话,即使 FileStream 不是使用 useAsync true 构造的,调用 Async API 方法也会导致使用正确的标志创建底层流?因为听起来我应该确保使用 FILE_FLAG_OVERLAPPED 选项创建 FileStream。
    • @AlexHopeO'Connor,不。它不会用那个标志打开它——但它仍然是异步的。 API 仍然返回 Task 并且它可以正常工作。
    【解决方案3】:

    所以我想知道您是否打算使用诸如 CopyToAsync 之类的方法,是否应该打开底层的 FileStream,如上所示?

    是的。原因主要是历史原因。

    首先,在 Windows 上,HANDLEs (including file handles) must be opened/created explicitly with an asynchronous flag if you want to do asynchronous (OVERLAPPED) operations on them。

    但是,the old Windows 95/98/ME line only supported asynchronous operations on serial port and IOCTL (device driver) handles。该平台系列不支持磁盘文件上的异步 I/O。而the original .NET did support 98/ME,所以原来的FileStream只是使用了同步I/O。我认为(但不是绝对肯定)Win98/ME 上的APM methods(如FileStream.BeginRead)可能只是使用所谓的"asynchronous delegates" 实现的(它只是执行一个同步方法,如FileStream.Read 在线程池线程上)。

    所以,这就是默认情况下不使用异步标志打开文件流句柄的历史原因。

    CopyToAsync 实际文档中的示例是如何演示的

    不幸的是,很多 MSDN 示例的质量都很差。如果您从“这是如何调用此特定方法的示例”的角度来处理它们,它们是可以的,但从“这是使用此方法的生产质量代码示例”的角度来看,它们就不是很好了。

    【讨论】:

      猜你喜欢
      • 2016-05-09
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2016-07-07
      • 2016-03-25
      • 2017-10-09
      相关资源
      最近更新 更多