次の方法で共有


ファイル アクセスに Async を使用する (Visual Basic)

非同期機能を使用してファイルにアクセスできます。 非同期機能を使用すると、コールバックを使用したり、コードを複数のメソッドまたはラムダ式に分割したりせずに、非同期メソッドを呼び出すことができます。 同期コードを非同期にするには、同期メソッドではなく非同期メソッドを呼び出し、いくつかのキーワードをコードに追加するだけです。

ファイル アクセス呼び出しに非同期性を追加する理由は次のとおりです。

  • 非同期では、操作を起動する 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 引数を指定します。

ファイル パスを指定して直接開く場合、 StreamReaderStreamWriter でこのオプションを使用することはできません。 ただし、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)取り消し」を参照してください。

こちらも参照ください