非同期機能を使用してファイルにアクセスできます。 非同期機能を使用すると、コールバックを使用したり、コードを複数のメソッドまたはラムダ式に分割したりせずに、非同期メソッドを呼び出すことができます。 同期コードを非同期にするには、同期メソッドではなく非同期メソッドを呼び出し、いくつかのキーワードをコードに追加するだけです。
ファイル アクセス呼び出しに非同期性を追加する理由は次のとおりです。
非同期では、操作を起動する UI スレッドが他の作業を実行できるため、UI アプリケーションの応答性が向上します。 UI スレッドで時間がかかるコードを実行する必要がある場合 (たとえば、50 ミリ秒を超える場合)、I/O が完了し、UI スレッドがキーボードやマウス入力などのイベントを再び処理できるようになるまで UI がフリーズする可能性があります。
非同期では、スレッドの必要性を減らすことで、ASP.NET やその他のサーバー ベースのアプリケーションのスケーラビリティが向上します。 アプリケーションが応答ごとに専用スレッドを使用し、1,000 個の要求が同時に処理されている場合は、1000 個のスレッドが必要です。 非同期操作では、多くの場合、待機中にスレッドを使用する必要はありません。 最後に既存の I/O 完了スレッドを短時間使用します。
ファイル アクセス操作の待機時間は、現在の条件下では非常に短くなる可能性がありますが、待ち時間は今後大幅に増加する可能性があります。 たとえば、世界中のサーバーにファイルを移動できます。
非同期機能を使用する場合の追加オーバーヘッドは小さくなります。
非同期タスクは、並列で簡単に実行できます。
例の実行
このトピックの例を実行するには、 WPF アプリケーション または Windows フォーム アプリケーション を作成し、ボタンを追加 します。 ボタンの Click
イベントで、各例の最初のメソッドへの呼び出しを追加します。
次の例では、次の Imports
ステートメントを含めます。
Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks
FileStream クラスの使用
このトピックの例では、 FileStream クラスを使用します。このクラスには、オペレーティング システム レベルで非同期 I/O が発生するオプションがあります。 このオプションを使用すると、多くの場合、ThreadPool スレッドをブロックしないようにすることができます。 このオプションを有効にするには、コンストラクター呼び出しで useAsync=true
または options=FileOptions.Asynchronous
引数を指定します。
ファイル パスを指定して直接開く場合、 StreamReader と StreamWriter でこのオプションを使用することはできません。 ただし、FileStream クラスが開いたStreamを指定する場合は、このオプションを使用できます。 UI スレッドが待機中にブロックされないため、ThreadPool スレッドがブロックされている場合でも、UI アプリでは非同期呼び出しが高速になります。
テキストの書き込み
次の例では、ファイルにテキストを書き込みます。 各 await ステートメントで、メソッドはすぐに終了します。 ファイル I/O が完了すると、await ステートメントの後のステートメントでメソッドが再開されます。 非同期修飾子は、await ステートメントを使用するメソッドの定義にあることに注意してください。
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
元の例にはステートメント Await sourceStream.WriteAsync(encodedText, 0, encodedText.Length)
があり、これは次の 2 つのステートメントの縮小です。
Dim theTask As Task = sourceStream.WriteAsync(encodedText, 0, encodedText.Length)
Await theTask
最初のステートメントはタスクを返し、ファイル処理を開始します。 await を含む 2 番目のステートメントにより、メソッドは直ちに終了し、別のタスクを返します。 後でファイル処理が完了すると、await の後のステートメントに実行が戻ります。 詳細については、「 非同期プログラムの制御フロー (Visual Basic)」を参照してください。
テキストの読み取り
次の例では、ファイルからテキストを読み取ります。 テキストはバッファーに格納され、この場合は StringBuilderに配置されます。 前の例とは異なり、await の評価では値が生成されます。
ReadAsync メソッドはTask<Int32>を返します。そのため、await の評価では、操作の完了後にInt32
値 (numRead
) が生成されます。 詳細については、「非同期の戻り値の型 (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
並列非同期 I/O
次の例では、10 個のテキスト ファイルを書き込む並列処理を示します。 各ファイルについて、 WriteAsync メソッドはタスクを返し、タスクの一覧に追加されます。
Await Task.WhenAll(tasks)
ステートメントはメソッドを終了し、すべてのタスクのファイル処理が完了するとメソッド内で再開します。
この例では、タスクの完了後、Finally
ブロック内のすべてのFileStream インスタンスを閉じます。
Imports
ステートメントで各FileStream
が作成された場合、タスクが完了する前にFileStream
が破棄される可能性があります。
パフォーマンスの向上は、ほぼ完全に並列処理からのものであり、非同期処理ではないことに注意してください。 非同期の利点は、複数のスレッドを関連付けず、ユーザー インターフェイス スレッドを関連付けない点です。
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
WriteAsyncメソッドとReadAsyncメソッドを使用する場合は、CancellationTokenを指定できます。これを使用して、ストリームの途中で操作を取り消すことができます。 詳細については、「マネージド スレッド での非同期アプリケーションのFine-Tuning (Visual Basic) と 取り消し」を参照してください。
こちらも参照ください
.NET