Asynchronous File I/O
非同步作業可讓您執行耗用大量資源的 I/O 作業,而不需要封鎖主執行緒。 這項效能考量對於 Windows 8.x Store 應用程式或傳統型應用程式而言特別重要,尤其是針對耗時的資料流作業可能會阻礙 UI 執行緒,使應用程式看起來像是停止運作的情況。
從 .NET Framework 4.5 開始,I/O 類型包含非同步方法,可簡化非同步作業。 非同步方法在其名稱中包含 Async
,例如 ReadAsync、 WriteAsync、 CopyToAsync、 FlushAsync、 ReadLineAsync和 ReadToEndAsync。 這些非同步方法會在 Stream 類別上實作 (例如 Stream、 FileStream和 MemoryStream),以及在用於從資料流讀取或寫入資料流的類別上實作 (例如 TextReader 和 TextWriter)。
在 .NET Framework 4 (含) 以前版本中,您必須使用方法 (例如 BeginRead 和 EndRead) 實作非同步 I/O 作業。 目前的 .NET 版本仍會提供這些方法,以支援舊版程式碼;不過,非同步方法可協助您更輕鬆地實作非同步 I/O 作業。
C# 和 Visual Basic 各有兩個進行非同步程式設計的關鍵字:
Async
(Visual Basic) 或async
(C#) 修飾詞,用來標記包含非同步作業的方法。Await
(Visual Basic) 或await
(C#) 運算子,會套用至非同步方法的結果。
若要實作非同步 I/O 作業,請搭配非同步方法使用這些關鍵字,如下列範例所示。 如需詳細資訊,請參閱使用 async 和 await 進行非同步程式設計 (C#) 或使用 Async 和 Await 進行非同步程式設計 (Visual Basic)。
下列範例示範如何使用兩個 FileStream 物件,以非同步方式將檔案從一個目錄複製到另一個目錄。 請注意, Click 控制項的 Button 事件處理常式由於會呼叫非同步方法,因此會以 async
修飾詞標記。
using System;
using System.Threading.Tasks;
using System.Windows;
using System.IO;
namespace WpfApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
string startDirectory = @"c:\Users\exampleuser\start";
string endDirectory = @"c:\Users\exampleuser\end";
foreach (string filename in Directory.EnumerateFiles(startDirectory))
{
using (FileStream sourceStream = File.Open(filename, FileMode.Open))
{
using (FileStream destinationStream = File.Create(Path.Combine(endDirectory, Path.GetFileName(filename))))
{
await sourceStream.CopyToAsync(destinationStream);
}
}
}
}
}
}
Imports System.IO
Class MainWindow
Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim StartDirectory As String = "c:\Users\exampleuser\start"
Dim EndDirectory As String = "c:\Users\exampleuser\end"
For Each filename As String In Directory.EnumerateFiles(StartDirectory)
Using SourceStream As FileStream = File.Open(filename, FileMode.Open)
Using DestinationStream As FileStream = File.Create(EndDirectory + filename.Substring(filename.LastIndexOf("\"c)))
Await SourceStream.CopyToAsync(DestinationStream)
End Using
End Using
Next
End Sub
End Class
下一個範例與前一個範例類似,但使用 StreamReader 和 StreamWriter 物件以非同步方式讀取及寫入文字檔的內容。
private async void Button_Click(object sender, RoutedEventArgs e)
{
string UserDirectory = @"c:\Users\exampleuser\";
using (StreamReader SourceReader = File.OpenText(UserDirectory + "BigFile.txt"))
{
using (StreamWriter DestinationWriter = File.CreateText(UserDirectory + "CopiedFile.txt"))
{
await CopyFilesAsync(SourceReader, DestinationWriter);
}
}
}
public async Task CopyFilesAsync(StreamReader Source, StreamWriter Destination)
{
char[] buffer = new char[0x1000];
int numRead;
while ((numRead = await Source.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
await Destination.WriteAsync(buffer, 0, numRead);
}
}
Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim UserDirectory As String = "c:\Users\exampleuser\"
Using SourceReader As StreamReader = File.OpenText(UserDirectory + "BigFile.txt")
Using DestinationWriter As StreamWriter = File.CreateText(UserDirectory + "CopiedFile.txt")
Await CopyFilesAsync(SourceReader, DestinationWriter)
End Using
End Using
End Sub
Public Async Function CopyFilesAsync(Source As StreamReader, Destination As StreamWriter) As Task
Dim buffer(4095) As Char
Dim numRead As Integer
numRead = Await Source.ReadAsync(buffer, 0, buffer.Length)
Do While numRead <> 0
Await Destination.WriteAsync(buffer, 0, numRead)
numRead = Await Source.ReadAsync(buffer, 0, buffer.Length)
Loop
End Function
下一個範例顯示程式碼後置檔案和 XAML 檔案,可用來開啟檔案作為 Windows 8.x Store 應用程式中的 Stream,並使用 StreamReader 類別的執行個體來讀取其內容。 它會使用非同步方法開啟檔案作為資料流並讀取其內容。
using System;
using System.IO;
using System.Text;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace ExampleApplication
{
public sealed partial class BlankPage : Page
{
public BlankPage()
{
this.InitializeComponent();
}
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
StringBuilder contents = new StringBuilder();
string nextLine;
int lineCounter = 1;
var openPicker = new FileOpenPicker();
openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".txt");
StorageFile selectedFile = await openPicker.PickSingleFileAsync();
using (StreamReader reader = new StreamReader(await selectedFile.OpenStreamForReadAsync()))
{
while ((nextLine = await reader.ReadLineAsync()) != null)
{
contents.AppendFormat("{0}. ", lineCounter);
contents.Append(nextLine);
contents.AppendLine();
lineCounter++;
if (lineCounter > 3)
{
contents.AppendLine("Only first 3 lines shown.");
break;
}
}
}
DisplayContentsBlock.Text = contents.ToString();
}
}
}
Imports System.Text
Imports System.IO
Imports Windows.Storage.Pickers
Imports Windows.Storage
NotInheritable Public Class BlankPage
Inherits Page
Private Async Sub Button_Click_1(sender As Object, e As RoutedEventArgs)
Dim contents As StringBuilder = New StringBuilder()
Dim nextLine As String
Dim lineCounter As Integer = 1
Dim openPicker = New FileOpenPicker()
openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary
openPicker.FileTypeFilter.Add(".txt")
Dim selectedFile As StorageFile = Await openPicker.PickSingleFileAsync()
Using reader As StreamReader = New StreamReader(Await selectedFile.OpenStreamForReadAsync())
nextLine = Await reader.ReadLineAsync()
While (nextLine <> Nothing)
contents.AppendFormat("{0}. ", lineCounter)
contents.Append(nextLine)
contents.AppendLine()
lineCounter = lineCounter + 1
If (lineCounter > 3) Then
contents.AppendLine("Only first 3 lines shown.")
Exit While
End If
nextLine = Await reader.ReadLineAsync()
End While
End Using
DisplayContentsBlock.Text = contents.ToString()
End Sub
End Class
<Page
x:Class="ExampleApplication.BlankPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ExampleApplication"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{StaticResource ApplicationPageBackgroundBrush}" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="Display lines from a file."></TextBlock>
<Button Content="Load File" Click="Button_Click_1"></Button>
<TextBlock Name="DisplayContentsBlock"></TextBlock>
</StackPanel>
</Page>