简体   繁体   中英

Cancel Async operation

private async void TriggerWeekChanged(Week currentWeek)
{
    await LoadDataForSelectedWeek(currentWeek); //Split into multiple methods 
}

In case a user hammers on the Change_Week Button how can I cancel the current Task, and start a new one with the new paramerters ?

I tried like this:

private async Task Refresh(Week selectedWeek, CancellationToken token)
{
    Collection.Clear();

    await LoadDataFromDatabase();

    token.ThrowIfCancellationRequested();

    await ApplyDataToBindings();

    token.ThrowIfCancellationRequested();

    //Do some other stuff
}

Problem is: In my Collection I got data from multiple weeks when I hit the button to fast in a row.

You did not mention about how LoadDataForSelectedWeek and Refresh interact.

For short you need to create one CancellationTokenSource instance that handle every click. Then pass it to that method and do Cancel method every time new one appear.

private async void TriggerWeekChanged(Week currentWeek, CancellationTokenSource tokenSource)
{
    tokenSource.Cancel();
    try
    {
        var loadDataTask = Task.Run(() => LoadDataForSelectedWeek(currentWeek, tokenSource.Token), tokenSource.Token); //Split into multiple methods
    }
    catch(OperationCanceledException ex)
    {
        //Cancelled
    }
}

LoadDataForSelectedWeek -> Refresh (?)

private async Task Refresh(Week selectedWeek, CancellationToken token)
{
    Collection.Clear();

    await LoadDataFromDatabase();

    token.ThrowIfCancellationRequested();

    await ApplyDataToBindings();

    token.ThrowIfCancellationRequested();

    //Do some other stuff
}

Everything that is awaited will need to have visibility on that CancellationToken . If it is a custom Task that you wrote, it should accept it as an argument, and periodically within the function, check to see if it has been cancelled. If so, it should take any actions needed (if any) to stop or rollback the operation in progress, then call ThrowIfCancellationRequested() .

In your example code, you propbably want to pass token in to LoadDataFromDatabase and ApplyDataToBindings , as well as any children of those tasks.

There may be some more advanced situations where you don't want to pass the same CancellationToken in to child tasks, but you still want them to be cancellable. In those cases, you should create a new, internal to the Task , Cancellationtoken that you use for child tasks.

An important thing to remember is that ThrowIfCancellationRequested marks safe places within the Task that the Task can be stopped. There is no guaranteed safe way for the runtime to automatically detect safe places. If the Task were to automatically cancel its self as soon as cancellation was requested, it could potentially be left in an unknown state, so it is up to developers to mark those safe locations. It isn't uncommon to have several calls to check cancellation scattered throughout your Task .


I've just notice that your TriggerWeekChanged function is async void . This is usually considered an anti-pattern when it is used on something that is not an event handler. It can cause a lot of problems with tracking the completed status of the async operations within the method, and handling any exceptions that might be thrown from within it. You should be very weary of anything that is marked as async void that isn't an event handler, as it is the wrong thing to do 99%, or more, of the time. I would strongly recommend changing that to async Task , and consider passing in the CancellationToken from your other code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM