取消异步操作

时间:2017-09-08 12:13:28

标签: c# wpf multithreading cancellationtokensource

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

如果用户敲击Change_Week按钮,如何取消当前任务,并使用新参数启动新任务?

我试过这样:

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

    await LoadDataFromDatabase();

    token.ThrowIfCancellationRequested();

    await ApplyDataToBindings();

    token.ThrowIfCancellationRequested();

    //Do some other stuff
}

问题是: 在我的Collection中,当我按下按钮快速连续多周时,我获得了数据。

2 个答案:

答案 0 :(得分:3)

等待的所有内容都需要具有CancellationToken的可见性。如果它是您编写的自定义Task,它应该接受它作为参数,并在函数内定期检查它是否已被取消。如果是这样,它应该采取任何所需的操作(如果有的话)来停止或回滚正在进行的操作,然后调用ThrowIfCancellationRequested()

在您的示例代码中,可能希望将token传递给LoadDataFromDatabaseApplyDataToBindings,以及这些任务的任何子项。

可能有一些更高级的情况,您不希望将相同的 CancellationToken传递给子任务,但您仍希望它们可以取消。在这些情况下,您应该创建一个新的内部TaskCancellationtoken,用于子任务。

要记住的一件重要事情是,ThrowIfCancellationRequested标记了TaskTask可以停止的安全位置。没有保证运行时自动检测安全位置的安全方法。如果Task在请求取消后立即自动取消其自身,则可能会将其置于未知状态,因此应由开发人员标记这些安全位置。在您的Task中分散检查取消的次数并不少见。

我刚注意到您的TriggerWeekChanged函数是async void。当它用于非事件处理程序的东西时,通常被认为是反模式。跟踪方法中async操作的已完成状态,并处理可能从其中抛出的任何异常,都会导致很多问题。你应该非常厌倦标记为async void并且不是事件处理程序的任何事情,因为做99%或更多时间是错误的。我强烈建议将其更改为async Task,并考虑从其他代码传递CancellationToken

答案 1 :(得分:2)

您没有提及LoadDataForSelectedWeekRefresh如何互动。

简而言之,您需要创建一个处理每次点击的CancellationTokenSource实例。然后将其传递给该方法,并在每次出现新方法时执行Cancel方法。

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 - >刷新(?)

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

    await LoadDataFromDatabase();

    token.ThrowIfCancellationRequested();

    await ApplyDataToBindings();

    token.ThrowIfCancellationRequested();

    //Do some other stuff
}