これは真の非同期メソッドです。
public Task<string> ProcessRequest()
{
var textFile = File.OpenText("file.txt");
var readTask = textFile.ReadToEndAsync();
readTask.ContinueWith(previousTask => textFile.Dispose());
return readTask;
}
大きなファイルまたは低速ドライブ上のファイルでこのメソッドを実行すると、ファイルの読み取りが終了するずっと前に実行が呼び出し元に戻ります。Stephen Cleary の例では、呼び出し元は、結果 ("foo") の計算が終了したときにのみ制御を取り戻します。
ファイルの読み取りが完了する前にメソッドの実行が呼び出し元に戻るため、ProcessRequest メソッドでファイルを閉じることができないため、Dispose は ContinueWith にある必要があります。
もちろん、自分のタスクを開始することもできます。
public Task<string> ProcessRequest(CancellationToken cancellationToken)
{
var readTask = Task.Run(() =>
{
using (var textFile = File.OpenText("file.txt"))
{
var text = textFile.ReadToEnd();
cancellationToken.ThrowIfCancellationRequested();
var processedText = text.Replace("foo", "bar");
return processedText;
}
});
return readTask;
}
CancellationToken を用意し、長時間実行されている操作をキャンセルできるように、キャンセルが要求されたかどうかを定期的に確認することをお勧めします。
編集 1
@Stephen Cleary が最初のサンプルを強調表示したため、この結果はほぼまたはまったく同じ CIL になります。
public async Task<string> ProcessRequest()
{
using (var textFile = File.OpenText("file.txt"))
{
var s = await textFile.ReadToEndAsync();
return s;
}
}
基本的に、コンパイラは await textFile.ReadToEndAsync() に続くコードを ContinueWith に変換します。
それぞれの構文には利点があります。私の好みは、1 ~ 2 行 (つまり、dispose と log) が ContinueWith に入り、より複雑な継続の使用が待っていることです。