I have async
method as shown below. I would like to have possibility to cancel
it when user decide to stop the job. How could i do that in new button CancelUpdate_Click
Private Async Sub btnUpdate_Click(sender As Object, e As EventArgs) Handles btnUpdate.Click
Await DoWorkAsync()
End Sub
Private Function DoWorkAsync() As Task(Of List(Of Vals))
Return _
Task.Run(
Async Function() _
_migrator.PrepareMigration(CInt(Invoke(New Func(Of Integer)(Function() lbNumms.SelectedValue))), CInt(Invoke(New Func(Of Integer)(Function() lbPro.SelectedValue)))))
End Function
Private Sub CancelUpdate_Click(sender As Object, e As EventArgs) Handles CancelUpdate.Click
'Stop DoWorkAsync if running
End Sub
UPDATED:
Private _tokenSource As New CancellationTokenSource()
Private ReadOnly _token = _tokenSource.Token
Private Async Sub btnUpdate_Click(sender As Object, e As EventArgs) Handles btnUpdate.Click
Try
Private ReadOnly _tokenSource As New CancellationTokenSource()
Private ReadOnly _token = _tokenSource.Token
Await DoWorkAsync()
Catch oe As OperationCanceledException
MessageBox.Show("Task has been cancelled")
Finally
_tokenSource.Dispose()
End Try
End Sub
Private Function DoWorkAsync() As Task(Of List(Of Vals))
Return _
Task.Run(
Function()
Dim tokenSource = New CancellationTokenSource()
Dim token = tokenSource.Token
Return _migrator.PrepareMigration(CInt(Invoke(New Func(Of Integer)(Function() lbNumms.SelectedValue))), CInt(Invoke(New Func(Of Integer)(Function() lbProducent.SelectedValue))), _token)
End Function)
End Function
Public Function PrepareMigration(value1 As Integer, value2 As Integer, cancellationToken As CancellationToken) As List(Of Vals))
cancellationToken.ThrowIfCancellationRequested()
MethodA()
cancellationToken.ThrowIfCancellationRequested()
MethodB()
End Function
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
_tokenSource.Cancel()
End Sub
CodePudding user response:
Cancellation isn't something that just magically works. It has to be explicitly coded. In this case, your PrepareMigration
routine needs to take a CancellationToken
parameter and needs to periodically check to see if that parameter's IsCancellationRequested
property has been set to True
.
One shortcut for doing this is to periodically call ThrowIfCancellationRequested
on the token; this will result in an OperationCanceledException
if the property is set.
If you are working in a routine that returns a Task
, you can return Task.FromCanceled
.
You would get the CancellationToken
to pass in from a CancellationTokenSource
. Then the event handler for the cancel button would call Cancel
on the source to signal the issued token.
For example, if you have processing that involves multiple steps, maybe it would work like this:
'Include other parameters as appropriate.
Sub PrepareMigration(ByVal token As CancellationToken)
DoStep1()
token.ThrowIfCancellationRequested()
DoStep2()
'...
token.ThrowIfCancellationRequested()
DoStepn()
End Sub
Or, if you have something where you are processing a set of items, maybe it would look like this:
Sub PrepareMigration(ByVal token As CancellationToken)
For Each item In itemsToProcess
token.ThrowIfCancellationRequested()
DoTheItem(item)
Next
End Sub
Mix and match as required. If a routine called in a particular step is especially long-running, that routine itself might need to be cancellation-aware.
Ultimately, where you put checks depends on the details of the steps of your calculation (including potentially "quick" vs "slow" steps) and what limits you want to set on how long a user has to wait until a requested cancellation takes effect.