Share via


使用 Async 以进行文件访问(C# 和 Visual Basic)

可以使用 异步 函数访问文件。 使用异步功能,可以调用异步方法,而不使用回调或拆分您在多个方法或 lambda 表达式中的代码。 若要使同步代码异步,则调用异步方法而不是一个同步方法并添加几个关键字到代码中。

您可能认为添加的以下原因 asynchrony 到文件访问调用:

  • Asynchrony 建议于应用程序的响应能力更强。,因为是一个操作的 UI 线程可以执行其他工作。 如果 UI 线程必须执行需要很长时间的代码(例如,超过 50 毫秒),UI 可以冻结,直到 I/O 完成的,并且用户界面线程可以重新处理键盘和鼠标输入和其他操作。

  • Asynchrony 通过减少对线程的需要增强 ASP.NET 和其他的可伸缩性基于服务器的应用程序。 如果应用程序使用专用线程上每个响应,并且一次两个请求同时处理,则有线程是必需的。 在等待期间,异步操作通常不需要使用线程。 它们简要使用现有的 I/O 完成线程在末尾。

  • 文件访问操作的延迟可能非常低在当前情况,但是,延迟在以后可能会大大提高。 例如,文件可能会移至位于 the world 的服务器。

  • 添加的开销使用异步功能很小。

  • 异步任务可以并行轻松地运行。

运行示例

备注

本主题中的示例不适用于 Windows 应用商店 app,是 Windows 8 app 是全屏和调整为连续交互。有关如何使用异步文件访问的信息。Windows 应用商店 app,请参见 .NET for Windows Store 应用程序概述文件和流 I/O。在 Windows 应用商店 app 的文件 I/O 的示例,您可以下载 文件 Access 示例

若要运行本主题中的示例,您可以创建 WPF 应用程序Windows 窗体应用程序 然后添加 按钮。 在按钮的 Click 事件,请向调用在每个示例中的第一个方法。

在下面的示例中,添加以下 Imports (Visual Basic)或 using (C#)语句。

Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;

对 FileStream 选件类的使用

本主题中的示例使用 FileStream 选件类,它具有一个选项导致异步 I/O 发生在操作系统级别。 使用此选项,您可以避免在许多情况下阻止线程池线程。 若要启用此选项,则指定 useAsync=true 或在构造函数中 options=FileOptions.Asynchronous 参数调用。

不能对 StreamReaderStreamWriter 的此选项,如果直接通过指定文件路径打开这些文件。 但是,您可以使用此选项,则提供自己 FileStream 选件类打开的 Stream。 请注意,异步调用是比在 UI app,即使线程池线程阻塞,在等待期间,因为用户界面线程未阻止。

编写文本

下面的示例写入文本到文件。 在每个请等待语句,则此方法会立即退出。 当文件 I/O 完成时,方法以等待语句后面的语句。 请注意"修饰符在使用等待语句方法的定义。

Public Async Sub ProcessWrite()
    Dim filePath = "temp2.txt"
    Dim text = "Hello World" & ControlChars.CrLf

    Await WriteTextAsync(filePath, text)
End Sub

Private Async Function WriteTextAsync(filePath As String, text As String) As Task
    Dim encodedText As Byte() = Encoding.Unicode.GetBytes(text)

    Using sourceStream As New FileStream(filePath,
        FileMode.Append, FileAccess.Write, FileShare.None,
        bufferSize:=4096, useAsync:=True)

        Await sourceStream.WriteAsync(encodedText, 0, encodedText.Length)
    End Using
End Function
public async void ProcessWrite()
{
    string filePath = @"temp2.txt";
    string text = "Hello World\r\n";

    await WriteTextAsync(filePath, text);
}

private async Task WriteTextAsync(string filePath, string text)
{
    byte[] encodedText = Encoding.Unicode.GetBytes(text);

    using (FileStream sourceStream = new FileStream(filePath,
        FileMode.Append, FileAccess.Write, FileShare.None,
        bufferSize: 4096, useAsync: true))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    };
}

原始示例使用语句 await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);,即以下两个语句的收缩:

Dim theTask As Task = sourceStream.WriteAsync(encodedText, 0, encodedText.Length)
Await theTask
Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
await theTask;

第一个语句返回任务并导致进程的文件。 使用等待的第二个语句导致方法立即退出并返回其他任务。 当随后处理的文件完成时,执行回遵循等待的语句。 有关更多信息,请参见异步程序中的控制流(C# 和 Visual Basic)演练:将调试器与异步方法一起使用

读取文本

下面的示例从文件中读取文本。 该文本缓冲区,因此,在这种情况下,将被放入 StringBuilder。 不同于在前面的示例中,等待的计算生成值。 ReadAsync 方法返回 Task<Int32>,因此,等待的计算生成一个 Int32 值(numRead),在操作完成之后。 有关更多信息,请参见异步返回类型(C# 和 Visual Basic)

Public Async Sub ProcessRead()
    Dim filePath = "temp2.txt"

    If File.Exists(filePath) = False Then
        Debug.WriteLine("file not found: " & filePath)
    Else
        Try
            Dim text As String = Await ReadTextAsync(filePath)
            Debug.WriteLine(text)
        Catch ex As Exception
            Debug.WriteLine(ex.Message)
        End Try
    End If
End Sub

Private Async Function ReadTextAsync(filePath As String) As Task(Of String)

    Using sourceStream As New FileStream(filePath,
        FileMode.Open, FileAccess.Read, FileShare.Read,
        bufferSize:=4096, useAsync:=True)

        Dim sb As New StringBuilder

        Dim buffer As Byte() = New Byte(&H1000) {}
        Dim numRead As Integer
        numRead = Await sourceStream.ReadAsync(buffer, 0, buffer.Length)
        While numRead <> 0
            Dim text As String = Encoding.Unicode.GetString(buffer, 0, numRead)
            sb.Append(text)

            numRead = Await sourceStream.ReadAsync(buffer, 0, buffer.Length)
        End While

        Return sb.ToString
    End Using
End Function
public async void ProcessRead()
{
    string filePath = @"temp2.txt";

    if (File.Exists(filePath) == false)
    {
        Debug.WriteLine("file not found: " + filePath);
    }
    else
    {
        try
        {
            string text = await ReadTextAsync(filePath);
            Debug.WriteLine(text);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

private async Task<string> ReadTextAsync(string filePath)
{
    using (FileStream sourceStream = new FileStream(filePath,
        FileMode.Open, FileAccess.Read, FileShare.Read,
        bufferSize: 4096, useAsync: true))
    {
        StringBuilder sb = new StringBuilder();

        byte[] buffer = new byte[0x1000];
        int numRead;
        while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
        {
            string text = Encoding.Unicode.GetString(buffer, 0, numRead);
            sb.Append(text);
        }

        return sb.ToString();
    }
}

并行异步 I/O

下面的示例通过编写 10 个文本文件演示并行处理。 对于每个文件,WriteAsync 方法返回然后添加到任务列表的任务。 文件时,在处理为所有完成的任务时,await Task.WhenAll(tasks); 语句退出方法并在方法中恢复。

在任务完成后,该示例以 finally 的所有 FileStream 实例块。 如果每 FileStream 在 using 语句中创建的,FileStream 可能已处理,在任务完成之前。

请注意所有性能提高几乎完全是从异步处理的并行处理而不是。 asynchrony 的优点是它不会占用多个线程,因此,它不会占用用户界面线程。

Public Async Sub ProcessWriteMult()
    Dim folder = "tempfolder\"
    Dim tasks As New List(Of Task)
    Dim sourceStreams As New List(Of FileStream)

    Try
        For index = 1 To 10
            Dim text = "In file " & index.ToString & ControlChars.CrLf

            Dim fileName = "thefile" & index.ToString("00") & ".txt"
            Dim filePath = folder & fileName

            Dim encodedText As Byte() = Encoding.Unicode.GetBytes(text)

            Dim sourceStream As New FileStream(filePath,
                FileMode.Append, FileAccess.Write, FileShare.None,
                bufferSize:=4096, useAsync:=True)

            Dim theTask As Task = sourceStream.WriteAsync(encodedText, 0, encodedText.Length)
            sourceStreams.Add(sourceStream)

            tasks.Add(theTask)
        Next

        Await Task.WhenAll(tasks)
    Finally
        For Each sourceStream As FileStream In sourceStreams
            sourceStream.Close()
        Next
    End Try
End Sub
public async void ProcessWriteMult()
{
    string folder = @"tempfolder\";
    List<Task> tasks = new List<Task>();
    List<FileStream> sourceStreams = new List<FileStream>();

    try
    {
        for (int index = 1; index <= 10; index++)
        {
            string text = "In file " + index.ToString() + "\r\n";

            string fileName = "thefile" + index.ToString("00") + ".txt";
            string filePath = folder + fileName;

            byte[] encodedText = Encoding.Unicode.GetBytes(text);

            FileStream sourceStream = new FileStream(filePath,
                FileMode.Append, FileAccess.Write, FileShare.None,
                bufferSize: 4096, useAsync: true);

            Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
            sourceStreams.Add(sourceStream);

            tasks.Add(theTask);
        }

        await Task.WhenAll(tasks);
    }

    finally
    {
        foreach (FileStream sourceStream in sourceStreams)
        {
            sourceStream.Close();
        }
    }
}

在使用 WriteAsyncReadAsync 方案时,可以指定 CancellationToken,可以使用取消操作中途。 有关更多信息,请参见微调异步应用程序(C# 和 Visual Basic)托管线程中的取消

请参见

任务

演练:将调试器与异步方法一起使用

概念

使用 Async 和 Await 的异步编程(C# 和 Visual Basic)

异步返回类型(C# 和 Visual Basic)

异步程序中的控制流(C# 和 Visual Basic)