Back to Top

プログラムの覚書

VB.NET Taskクラスで複数タスクを非同期的に待つ

Taskクラスで、複数のタスクを起動し、タスクの処理が終了するまで、非同期的に待つ方法を記載します。

非同期的に待つ方法には Async と AWait を使用します。

Task.WhenAllメソッドを使用して、全てのタスクが終了するまで待つ

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    DirectCast(sender, Button).Enabled = False

    Dim task1 As Task = task.Run(
        Sub()
            System.Threading.Thread.Sleep(10000)    '重たい処理
        End Sub
    )

    Dim task2 As Task = Task.Run(
        Sub()
            System.Threading.Thread.Sleep(5000)     '重たい処理
        End Sub
    )

    Await Task.WhenAll(task1, task2)                '全タスクが終了するまで非同期的に待つ

    DirectCast(sender, Button).Enabled = True
End Sub

全てのタスクの処理が終了すると、Await以下の処理を実行します。

以下のようにListを使用して書くことも出来ます。

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    DirectCast(sender, Button).Enabled = False

    Dim tasks As New List(Of Task)

    tasks.Add(Task.Run(
        Sub()
            System.Threading.Thread.Sleep(5000)    '重たい処理
        End Sub
    ))

    tasks.Add(Task.Run(
        Sub()
            System.Threading.Thread.Sleep(5000)    '重たい処理
        End Sub
    ))

    Await Task.WhenAll(tasks)                       '全タスクが終了するまで非同期的に待つ

    DirectCast(sender, Button).Enabled = True
End Sub

 

Task.WhenAnyメソッドを使用して、どれかのタスクが終了するまで待つ

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    DirectCast(sender, Button).Enabled = False

    Dim tasks As New List(Of Task)

    tasks.Add(Task.Run(
        Sub()
            System.Threading.Thread.Sleep(10000)    '重たい処理
            Console.WriteLine("Task End1")
        End Sub
    ))

    tasks.Add(Task.Run(
        Sub()
            System.Threading.Thread.Sleep(5000)    '重たい処理
            Console.WriteLine("Task End2")
        End Sub
    ))

    Await Task.WhenAny(tasks)                      'どれかタスクが終了するまで非同期的に待つ

    Console.WriteLine("Task End")

    DirectCast(sender, Button).Enabled = True
End Sub

上記は、どちらかのタスクの処理が終了するとAwait以下の処理を実行します。

 

VB.NET Taskクラスの処理待ちの方法

Taskクラスを使用して非同期処理をする際、同期をとるためのタスク待ちについて記載します。

ここでは、非同期の処理待ちをする事ができる Async & AWait  と Wait()メソッドを説明します。

プログラムを起動したときのメインタスクは、UIタスク(ユーザインターフェースタスク)で、Taskクラスで起動するタスクは、ローカルタスクと以下記載します。

Async と AWait でのタスク待ち

GUIでボタンを押して重たい処理する場合を例にします。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    System.Threading.Thread.Sleep(10000)     '重たい処理
End Sub

以上の処理を実行すると、処理が終了するまで、フォーム移動などの操作が出来なくなります。

次にTaskクラスを使用して処理を実行します。

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    DirectCast(sender, Button).Enabled = False        'ボタンを選択できないようにする

    Dim task As Task = Task.Run(
        Sub()
            System.Threading.Thread.Sleep(10000)      '重たい処理
        End Sub
    )
    Await task                                        '処理が完了するのを非同期的に待つ

    MessageBox.Show("Task End")

    DirectCast(sender, Button).Enabled = True
End Sub

Async & Await の非同期メソッドで、UIスレッドに同期的な継続処理に自動変換されます。

上記では、ローカルタスクで処理が実行されるので、ボタンの選択は処理が終わるまで出来ませんが、フォームの操作は出来ます。

Await で処理を止めているのではなく、非同期的に待っているので、UIタスクに処理を戻します。

ローカルタスクでの処理が終了したら、MessageBox.Show(“Task End”)が実行されます。

上記のコードの省略化

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    DirectCast(sender, Button).Enabled = False        'ボタンを選択できないようにする

    Await Task.Run(
        Sub()
            System.Threading.Thread.Sleep(10000)      '重たい処理
        End Sub
    )
    MessageBox.Show("Task End")

    DirectCast(sender, Button).Enabled = True
End Sub

戻り値がある場合

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    DirectCast(sender, Button).Enabled = False        'ボタンを選択できないようにする

    Dim result As Boolean = Await Task.Run(
        Function() As Boolean
            System.Threading.Thread.Sleep(5000)
            Return True
        End Function
    )
    MessageBox.Show("Task End")

    DirectCast(sender, Button).Enabled = True
End Sub

・Task.Run()の場合は、Task(Of)の戻り値になります。

・Await Task.Run()の場合は、指定した戻り値になります。

 

Waitメソッドでの処理待ち

GUIで Wait() メソッドを使用することは、通常ありません。

以下のようにCUIの時、Awaitをおこなっても処理が終了しまう場合、に使用します。

Imports System.Threading

Module Module1

    Sub Main()
        Dim task As Task = Task.Run(
            Sub()
                System.Threading.Thread.Sleep(5000)      '重たい処理
                Console.WriteLine("Task End")
            End Sub
        )
        task.Wait()

        Console.WriteLine("End")
    End Sub

End Module