Back to Top

プログラムの覚書

Category: スレッド・タスク

VB.NET Taskクラスの処理キャンセル

タスクを実行した際、途中でキャンセルしたいことがあります。その際の方法を記載します。

Taskクラス によるタスクキャンセルは、CancellationTokenSourceクラスを使用します。

タスクキャンセル要求は、CancellationTokenSource.Cancel()メソッドを実行します。

これによりキャンセルフラグが立ち、このフラグを参照することによりキャンセル処理を実行します。

ThrowIfCancellationRequested()によるキャンセル方法

オペレーション側プログラム

Dim cts As CancellationTokenSource

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    cts = New CancellationTokenSource()    
    Try
        Dim t = ActionAsync(cts.Token)          'タスクの実行
        Await t                                 '非同期的に完了待ち
    Catch ex As OperationCanceledException
        MessageBox.Show("キャンセルされました")
    End Try
End Sub

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    cts.Cancel()        'タスクのキャンセル要求
End Sub

タスク側プログラム(ThrowIfCancellationRequestedメソッド抜け出し)

Async Function ActionAsync(token As CancellationToken) As Task(Of Boolean)
    Dim result As Boolean = Await Task.Run(
         Function() As Boolean
             For i As Integer = 0 To 100
                 token.ThrowIfCancellationRequested()           'キャンセル時の処理
                 Thread.Sleep(1000)
             Next
             Return True
         End Function
    )

    MessageBox.Show("タスク終了")
    Return result
End Function

CancellationToken構造体のThrowIfCancellationRequested()メソッドで、キャンセルフラグをチェックします。

また、実際のキャンセルフラグは、CancellationToken.IsCancellationRequestedプロパティに設定されていて

このフラグが立ったらタスク側は、OperationCanceledException例外をスローするように作成します。

キャンセルフラグの例外スローのコードは、以下のコードのようになります。

If (token.IsCancellationRequested) Then
    Throw New OperationCanceledException(token)
End If

上記を一文で書けるようにメソッドが用意されています。

token.ThrowIfCancellationRequested()

 

例外のスローではない IsCancellationRequestedでのキャンセル抜け出し

ThrowIfCancellationRequested()での抜け出しは、 例外スローするので、上に記載したコードではキャンセルの際、MessageBox.Show(“タスク終了”)実行されません。

そこで、IsCancellationRequestedを参照して抜け出すようにします。

タスク側プログラム(IsCancellationRequestedプロパティ抜け出し)

Async Function ActionAsync(token As CancellationToken) As Task(Of Task)
    Dim result As Task = Task.Run(
         Sub()
             While True
                 If (token.IsCancellationRequested) Then     'キャンセル時の処理
                    Exit While
                 End If
                 Thread.Sleep(1000)
             End While
         End Sub
    )
    Await result

    MessageBox.Show("タスク終了")
    Return result
End Function

 

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以下の処理を実行します。