Почему эта отмененная задача выполняется до конца - PullRequest
1 голос
/ 24 сентября 2019

У меня есть запрос, который требует много времени для возврата, поэтому я вставляю функцию с возвратом данных в функцию внутри задачи, например, так:

Dim tokenSource As CancellationTokenSource

Private Async Sub btnSomeThing() Handles btnSomething.Click

    tokenSource = New CancellationTokenSource

    Dim cancellationToken As CancellationToken = tokenSource.Token

    Dim t As Task(Of DataTable) = Task.Run(Function()
                                               Return someFunction.GetDataTable()
                                           End Function,
                                           cancellationToken)
    Await t

    Try
        If t.Result.Rows.Count > 0 Then
            ' Display the datatable
        End If
    Catch e As Exception
        If TypeOf e IsNot TaskCanceledException Then
            MessageBox.Show("Error")
        End If
    Finally
        tokenSource.Dispose()
    End Try

End Sub

И я пытаюсь отменить задачу с помощьювызов tokenSource.Cancel() в другом месте.

Однако, что я вижу, это то, что когда Return someFunction.GetDataTable() в конечном итоге возвращается, код продолжает выполняться, как будто эта задача не была отменена.

Я понимаю,что someFunction.GetDataTable() будет продолжаться до тех пор, пока это не будет сделано, поскольку нет хорошего способа остановить вызов базы данных после его запуска, но когда он все-таки возвращает, не должно вызывать задачу t выдает TaskCanceledException, и поэтому остальная часть процедуры пропускается какчасть Try-Catch?

Как правильно отменить t?

РЕДАКТИРОВАТЬ:

Ниже приведен код сейчас.Исключение из cancellationToken.ThrowIfCancellationRequested() не «перехватывается», и поэтому приложение просто вводит режим останова.Я также пытался дозвониться до cancellationToken.ThrowIfCancellationRequested() в течение Task.Run, после Await t, но все они имеют одинаковый эффект.После вызова cancellationToken.ThrowIfCancellationRequested() приложение вылетает.

Dim tokenSource As CancellationTokenSource

Private Async Sub btnSomeThing() Handles btnSomething.Click

    tokenSource = New CancellationTokenSource

    Dim cancellationToken As CancellationToken = tokenSource.Token

    Dim t As Task(Of DataTable) = Task.Run(Function()
                                               Return GetDataTable(cancellationToken)
                                           End Function,
    Try                                       cancellationToken)
        Await t

        If t.Result.Rows.Count > 0 Then
            ' Display the datatable
        End If
    Catch ex As AggregateException
        For Each item In ex.InnerExceptions
            Debug.WriteLine(ex.Message & " " & item.Message)
        Next
    Finally
        tokenSource.Dispose()
    End Try

End Sub

Public Function GetDataTable (cancellationToken As CancellationToken)

    Dim dt As DataTable

    'fill dt from query with DataAdapter

    cancellationToken.ThrowIfCancellationRequested()

    Return dt

End Function

1 Ответ

4 голосов
/ 24 сентября 2019

Нет способа просто убить задачу извне.Вот почему существует понятие CancellationToken.Недостаточно просто передать этот токен задаче, как вы делаете в своем коде.Вы должны написать свою собственную логику внутри этой задачи для обработки запроса на отмену.

Поскольку вы используете свою задачу только для вызова более глубокой функции (someFunction.GetDataTable()), вы должны реализовать поддержку отмены для этой функции какЧто ж.Т.е. передать токен отмены, который вы получили в задании, в эту функцию, а затем сделать что-то вроде этого (псевдокод):

Function GetDataTable(ct As CancellationToken)
    ResultSet = Empty
    While DatabaseTable.HasMoreRecords
        ResultSet.Add DatabaseTable.ReadNext100Records()
        If ct.IsCancellationRequested Then
            ' Do the logic you want to perform when the query should be cancelled
            ' e.g. close db connection or such things

            ' Then call this function to raise the Exception
            ct.ThrowIfCancellationRequested()
        End If
    End While
    Return ResultSet
End Function

Так сказать словами, создать цикл, прочитать только небольшое подмножество строк ипроверьте, запрашивается ли отмена, прежде чем читать следующие записи.

Для справки посмотрите здесь

Изменить, чтобы ответить на ваш комментарий здесь: Ссылочная ссылка дает вам хорошийпример того, как обрабатывать это исключение.Подводя итог, вы должны обернуть try-catch вокруг в ожидании задания:

Try
   Await t
   If t.Result.Rows.Count > 0 Then
       ' Display the datatable
   End If
Catch e As Exception
    If TypeOf e IsNot OperationCanceledException Then
        MessageBox.Show("Error")
    End If
Finally
    tokenSource.Dispose()
End Try
...