文件
-
瞭解如何使用取消令牌向工作清單發出取消要求訊號。
-
了解如何在 C# 中使用 Task.WhenAny 來啟動多個工作,並在完成時處理其結果,而不是依順序開始加以處理。
-
瞭解異步方法在 C# 中可以擁有的傳回型別,以及每個類型的程式代碼範例。
您可以使用非同步功能來存取檔案。 使用非同步功能,您就可以呼叫非同步方法,而不需要使用回呼或將您的程式碼分散到多種方法或 Lambda 運算式上。 若要讓同步程式碼變成非同步,只要呼叫非同步方法 (而不是同步方法),然後將幾個關鍵字新增至程式碼即可。
您可能會基於下列原因將非同步功能新增至檔案存取呼叫:
本主題中的簡單範例示範 File.WriteAllTextAsync 和 File.ReadAllTextAsync。 如果要精細控制檔案 I/O 作業,請使用 FileStream 類別,其中會有導致作業系統層級發生非同步 I/O 的選項。 在許多情況下,您可以使用此選項來避免封鎖 ThreadPool 執行緒。 若要啟用此選項,請在建構函式呼叫中指定 useAsync=true
或 options=FileOptions.Asynchronous
引數。
如果您藉由指定檔案路徑來直接開啟 StreamReader 和 StreamWriter,就無法搭配使用這個選項。 不過,如果您提供它們已開啟 FileStream 類別的 Stream,則可以使用此選項。 即使已封鎖 ThreadPool 執行緒,非同步呼叫在 UI 應用程式中還是更快,因為等候期間並不會封鎖 UI 執行緒。
下列範例會將文字寫入檔案。 在每個 await 陳述式中,此方法會立即結束。 當檔案 I/O 完成時,此方法會在 await 陳述式後面的陳述式繼續進行。 使用 await 陳述式的方法定義中會有 async 修飾詞。
public async Task SimpleWriteAsync()
{
string filePath = "simple.txt";
string text = $"Hello World";
await File.WriteAllTextAsync(filePath, text);
}
public async Task ProcessWriteAsync()
{
string filePath = "temp.txt";
string text = $"Hello World{Environment.NewLine}";
await WriteTextAsync(filePath, text);
}
async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
原始範例具有陳述式 await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
,這是下列兩個陳述式的縮減:
Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
await theTask;
第一個陳述式會傳回一個工作並開始處理檔案。 第二個陳述式具有 await,會立即結束方法並傳回其他工作。 稍後完成處理檔案之後,則會回到 await 後面的陳述式繼續執行。
下列範例會從檔案讀取文字。
public async Task SimpleReadAsync()
{
string filePath = "simple.txt";
string text = await File.ReadAllTextAsync(filePath);
Console.WriteLine(text);
}
此文字已經過緩衝處理,在本例中已置於 StringBuilder。 不同於上一個範例,await 的評估會產生一個值。 ReadAsync方法會傳回Task<Int32>,因此在作業完成之後,await 的評估會產生Int32
值numRead
。 如需詳細資訊,請參閱非同步方法的傳回型別 (C#)。
public async Task ProcessReadAsync()
{
try
{
string filePath = "temp.txt";
if (File.Exists(filePath) != false)
{
string text = await ReadTextAsync(filePath);
Console.WriteLine(text);
}
else
{
Console.WriteLine($"file not found: {filePath}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
async Task<string> ReadTextAsync(string filePath)
{
using var sourceStream =
new FileStream(
filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true);
var 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();
}
下列範例示範如何平行處理寫入 10 個文字檔的作業。
public async Task SimpleParallelWriteAsync()
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
for (int index = 11; index <= 20; ++ index)
{
string fileName = $"file-{index:00}.txt";
string filePath = $"{folder}/{fileName}";
string text = $"In file {index}{Environment.NewLine}";
writeTaskList.Add(File.WriteAllTextAsync(filePath, text));
}
await Task.WhenAll(writeTaskList);
}
針對每個檔案,WriteAsync 方法會傳回一個工作,此工作之後會新增至工作清單。 await Task.WhenAll(tasks);
陳述式會在所有工作的檔案處理完成時結束方法,並在方法內繼續進行。
此範例會在工作完成之後,關閉 finally
區塊中的所有 FileStream 執行個體。 如果改為在 using
陳述式中建立每個 FileStream
,FileStream
可能會在工作完成前就被處置。
任何效能提升幾乎完全來自平行處理,而不是非同步處理。 非同步的優點在於不會佔用多個執行緒,而且不會佔用使用者介面執行緒。
public async Task ProcessMultipleWritesAsync()
{
IList<FileStream> sourceStreams = new List<FileStream>();
try
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
for (int index = 1; index <= 10; ++ index)
{
string fileName = $"file-{index:00}.txt";
string filePath = $"{folder}/{fileName}";
string text = $"In file {index}{Environment.NewLine}";
byte[] encodedText = Encoding.Unicode.GetBytes(text);
var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
Task writeTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
sourceStreams.Add(sourceStream);
writeTaskList.Add(writeTask);
}
await Task.WhenAll(writeTaskList);
}
finally
{
foreach (FileStream sourceStream in sourceStreams)
{
sourceStream.Close();
}
}
}
使用 WriteAsync 和 ReadAsync 方法時,您可以指定 CancellationToken,這可用來在中途取消作業。 如需詳細資訊,請參閱受控執行緒中的取消作業。
文件
瞭解如何使用取消令牌向工作清單發出取消要求訊號。
了解如何在 C# 中使用 Task.WhenAny 來啟動多個工作,並在完成時處理其結果,而不是依順序開始加以處理。
瞭解異步方法在 C# 中可以擁有的傳回型別,以及每個類型的程式代碼範例。
訓練
學習路徑
Access local files asynchronously - Training
Learn how to manage local files using the System.IO namespace and how to asynchronously back up and restore application data using the System.Text.Json namespace.