VB.NET 非同期 I/O
(Visual Studio 2015 版)

投稿日 2016/04/06

[Home] 

基本

非同期 I/O メソッドを使うメリット

非同期 I/O メソッドを使うと、時間のかかる入出力の間に別の処理を並行して実行できます。特に、リモートファイルのアクセスなどは、決まった時間内にアクセスが終了する保証がないので、非同期 I/O メソッドを使ってアクセスしないと、アプリケーションが固まってしまったように見えることがあります。


非同期 I/O メソッドの動作

非同期 I/O メソッドを呼び出すことで、ただちに入出力処理が開始するのではありません。非同期 I/O メソッドは入出力処理の指示を OS に出すだけで終了してしまいます。その際に、「I/O が完了したらこのメソッドをコールするように」(コールバック)という指示も同時に出します。

実際に I/O が完了した時点でそのコールバックメソッドが呼び出され、受け取ったデータを処理するなり保存するなりの処理を行います。I/O が完了するまでの時間は、別の処理を行うか待機することになります。

待機中に既定時間を超えた場合などには、I/O 処理の取消も行うことができます。それには非同期 I/O メソッドを呼び出したときに受け取る「取消トークン」を使って、取り消しメソッドを呼び出すことで行います。


Async / Await キーワード

.NET Framework 4.5 以上では、Async / Await キーワードがサポートされていて、独自の非同期メソッドを定義して使うことができます。

.NET Framework のストリームなどで用意されている非同期 I/O メソッドを使うだけなら、このキーワードを使う必要はありません。


非同期 I/O メソッドの種類

非同期 I/O メソッドには 2 種類あります。ひとつは .NET Framework 4.0 以前のバージョンでも動作するもので、Task クラスを使用しないので使用は面倒です。もうひとつは、.NET Framework 4.5 以後のバージョンで動作するメソッドで、Task クラスを使用するので簡単に非同期 I/O を記述できます。よって、非同期 I/O を使う場合は、.NET Framework 4.5 以後のバージョンを使うほうが簡単になります。

次のメソッドは System.IO.FileStream の非同期 I/O メソッドの一部です。

 

ストリームの非同期入出力サンプル

FileStream.ReadAsync メソッド

Imports System.IO

Module Module1
    ''' <summary>
    ''' File.ReadAsync method
    ''' </summary>
    Sub Main(ByVal args As String())
        If args.Length = 0 Then
            Console.WriteLine("エラー: ファイル名を指定してください。")
#If DEBUG Then
            Console.ReadKey()
#End If
            Exit Sub
        End If

        Try
            Using reader = File.OpenRead(args(0))
                Dim buffer(10000) As Byte
                Dim task As Task(Of Integer) = reader.ReadAsync(buffer, 0, buffer.Length)
                task.Wait()
                For i = 1 To task.Result
                    Console.Write("{0:x2} ", buffer(i))
                Next
            End Using
            Console.WriteLine()
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try

        Console.WriteLine("終わり。")
#If DEBUG Then
        Console.ReadKey()
#End If
    End Sub

End Module

 

FileStream.WriteAsync メソッド

Imports System.IO

Module Module1
    ''' <summary>
    ''' File.WriteAsync method
    ''' </summary>
    Sub Main(ByVal args() As String)
        If args.Length = 0 Then
            Console.WriteLine("エラー: ファイル名を指定してください。")
#If DEBUG Then
            Console.ReadKey()
#End If
            Exit Sub
        End If

        Dim buffer() As Byte = {&H10, &H11, &H12, &H13, &H14, &H15}

        Try
            Using writer = File.Create(args(0))
                Dim task As Task = writer.WriteAsync(buffer, 0, buffer.Length)
                task.Wait()
            End Using
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try

        Console.WriteLine("終わり。")
#If DEBUG Then
        Console.ReadKey()
#End If
    End Sub

End Module


 

FileStream.CopyToAsync メソッド

Imports System.IO

Module Module1
    ''' <summary>
    ''' CopyToAsync method
    ''' </summary>
    Sub Main(ByVal args() As String)
        If args.Length < 2 Then
            Console.WriteLine("エラー: ファイル名を指定してください。")
#If DEBUG Then
            Console.ReadKey()
#End If
            Exit Sub
        End If

        Dim src = args(0)
        Dim dest = args(1)

        Try
            Dim reader = File.OpenRead(src)
            Dim writer = File.Create(dest)

            Dim task = reader.CopyToAsync(writer)
            task.Wait()
            Console.WriteLine("コピー終わり。")
        Catch ex As Exception
            Console.WriteLine("エラー:" & ex.Message)
        End Try

#If DEBUG Then
        Console.ReadKey()
#End If
    End Sub

End Module

 

FileStream.BeginRead, FileStream.EndRead メソッド

Imports System.IO

Module Module1

    Dim n As Integer = 0
    Dim buffer(10000) As Byte
    Dim reader As FileStream

    ''' <summary>
    ''' File.BeginRead method
    ''' </summary>
    Sub Main(ByVal args() As String)
        If args.Length = 0 Then
            Console.WriteLine("エラー: ファイル名を指定してください。")
#If DEBUG Then
            Console.ReadKey()
#End If
            Exit Sub
        End If

        Dim state As New Object

        Try
            reader = File.OpenRead(args(0))
            Dim result As IAsyncResult = reader.BeginRead(buffer, 0, buffer.Length, AddressOf callBack, state)
            Do Until result.IsCompleted
                Threading.Thread.Sleep(100)
            Loop
        Catch ex As Exception
            Console.WriteLine("エラー:" & ex.Message)
        End Try

        Console.WriteLine("終わり。")
#If DEBUG Then
        Console.ReadKey()
#End If
    End Sub

    ''' <summary>
    ''' コールバック
    ''' </summary>
    ''' <param name="ar"></param>
    Sub callBack(ByVal ar As IAsyncResult)
        Console.WriteLine("コールバック")
        n = reader.EndRead(ar)
        For i = 0 To n - 1
            Console.Write("{0:x2} ", buffer(i))
        Next
        Console.WriteLine()
        reader.Close()
    End Sub
End Module

 

Async / Await の使用例

Imports System.IO

' VB の非同期メソッドのテスト
'   C#同様、Async/Awaitキーワードが用意されている。
Module Module1
    ' メインプログラム
    Sub Main(ByVal args As String())
        If args.Length = 0 Then
            Console.WriteLine("Usage: VBAsyncIO.exe filename")
            Return
        End If

        Dim task As Task = ReadFile(args(0))
        task.Wait()

        Console.WriteLine()
        Console.WriteLine("Done.")
#If DEBUG Then
        Console.ReadKey()
#End If
    End Sub

    ''' <summary>
    ''' 非同期メソッドを使うためにAsyncキーワードを使ってメソッドを用意する。
    ''' </summary>
    ''' <param name="filename"></param>
    ''' <remarks></remarks>
    Async Function ReadFile(ByVal filename As String) As Task
        Dim taskStr As Task(Of String)
        Dim reader = New StreamReader(filename)

        Do While Not reader.EndOfStream
            ' Awaitでラムダ式を実行する。
            Await Task.Run(
                Sub()
                    taskStr = reader.ReadLineAsync()
                    Console.WriteLine(taskStr.Result)
                End Sub
            )
        Loop
    End Function  ' FunctionだがReturn文は不要。

End Module

 

CancellationToken の使用例

Imports System.IO

Module Module1
    ''' <summary>
    ''' File.ReadAsync method
    ''' </summary>
    Sub Main(ByVal args As String())
        If args.Length = 0 Then
            Console.WriteLine("エラー: ファイル名を指定してください。")
#If DEBUG Then
            Console.ReadKey()
#End If
            Exit Sub
        End If

        Console.WriteLine(args(0))

        ' CancellationTokenSource を作成
        Dim cancelSrc As New Threading.CancellationTokenSource

        Try
            Using reader = File.OpenRead(args(0))

                Dim buffer(10000) As Byte

                ' 読み出しタスクを取得
                Dim task As Task(Of Integer) = reader.ReadAsync(buffer, 0, buffer.Length)

                ' すぐに取消
                cancelSrc.Cancel()

                ' Wait メソッドにトークンを渡す。
                task.Wait(cancelSrc.Token)

                ' 結果を表示
                For i = 1 To task.Result
                    Console.Write("{0:x2} ", buffer(i))
                Next
            End Using

            Console.WriteLine()
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        Finally
            ' Dispose() を実行する必要がある。
            cancelSrc.Dispose()
        End Try

        Console.WriteLine("終わり。")
#If DEBUG Then
        Console.ReadKey()
#End If
    End Sub

End Module






 

 


 

 開設 2014年12月   著作権 2014-2015 bonk.red  連絡先: こちらからメッセージを送ってください。

 このページの先頭へ..