文件流.ReadAsync有时同步完成

本文关键字:同步 ReadAsync 文件 | 更新日期: 2023-09-27 17:50:55

试试吧。设置一个带有单个按钮的新Windows窗体应用程序,并为按钮单击事件设置以下代码:

private async void button1_Click(object sender, EventArgs e)
{
    using (var file = File.OpenRead(@"C:'Temp'Sample.txt"))
    {
        byte[] buffer = new byte[4096];
        int threadId = Thread.CurrentThread.ManagedThreadId;
        int read = await file.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
        Debug.Assert(threadId != Thread.CurrentThread.ManagedThreadId);
    }
}

然后运行应用程序并快速点击按钮。如果你的经验和我一样,你会发现有时它会像预期的那样工作,然而,其他时候Debug.Assert会失败。

根据我对Stephen Cleary博客上解释的ConfigureAwait的理解,将false传递给continueOnCapturedContext应该指示任务不同步回"主"上下文(在这种情况下是UI线程),并且执行应该继续在线程池上。

为什么断言会随机失败呢?我只能假设ReadAsync有时不会在后台线程上完成,即它将同步完成。

此行为符合KB 156932 -异步磁盘I/O在Windows上显示为同步:

大多数I/O驱动程序(磁盘、通信和其他)都有特殊情况代码,如果一个I/O请求可以"立即"完成操作将完成,ReadFile或WriteFile函数将返回TRUE。从各个方面来看,这些类型的操作似乎是同步的。对于磁盘设备,I/O请求通常可以是当数据被缓存到内存中时"立即"完成。

我的假设和测试应用是否正确?ReadAsync方法有时可以同步完成吗?是否有一种方法来保证执行将始终继续在后台线程?

这个问题对我的应用程序造成了严重破坏,它使用COM对象,需要我知道哪个线程一直在执行。

Windows 7 64位,.NET 4.5.1

文件流.ReadAsync有时同步完成

ReadAsync方法有时可以同步完成吗?

是的。这并不罕见,因为在操作系统级别和。net流类型中都有缓冲区。

是否有一种方法可以保证执行始终在后台线程上继续?

如果您总是希望代码在线程池线程上执行,则使用Task.Run:

private async void button1_Click(object sender, EventArgs e)
{
  using (var file = File.OpenRead(@"C:'Temp'Sample.txt"))
  {
    byte[] buffer = new byte[4096];
    int threadId = Thread.CurrentThread.ManagedThreadId;
    int read = await file.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
    await Task.Run(() =>
    {
      Debug.Assert(threadId != Thread.CurrentThread.ManagedThreadId);
    }).ConfigureAwait(false);
  }
}

ConfigureAwait是一个优化提示,而不是移动到后台线程的命令。如果操作已经完成,ConfigureAwait没有作用。在这种情况下,await遵循"快速路径",这实际上意味着它同步地继续,因此任何ConfigureAwait提示都将被忽略。

如果文件读取在调用await之前同步完成,我相当确定没有任何异步编译器魔法发生,并且调用线程上的执行继续同步。如果我想保证它在线程池线程上启动,我会使用Task.Run(() => file.ReadAsync(buffer, 0, buffer.Length))将工作排队到线程池。

当您在Task上使用await时,如果任务已经完成,代码将同步执行。然后在该任务上调用ConfigureAwait将没有什么区别,因为不会涉及其他线程。

听起来好像如果数据在内存中(因为您正在重复读取相同的文件)file.ReadAsync可以同步完成,因此它将在同一线程上完成。