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
中,当我按下按钮快速连续多周时,我获得了数据。
答案 0 :(得分:3)
等待的所有内容都需要具有CancellationToken
的可见性。如果它是您编写的自定义Task
,它应该接受它作为参数,并在函数内定期检查它是否已被取消。如果是这样,它应该采取任何所需的操作(如果有的话)来停止或回滚正在进行的操作,然后调用ThrowIfCancellationRequested()
。
在您的示例代码中,可能希望将token
传递给LoadDataFromDatabase
和ApplyDataToBindings
,以及这些任务的任何子项。
可能有一些更高级的情况,您不希望将相同的 CancellationToken
传递给子任务,但您仍希望它们可以取消。在这些情况下,您应该创建一个新的内部Task
,Cancellationtoken
,用于子任务。
要记住的一件重要事情是,ThrowIfCancellationRequested
标记了Task
中Task
可以停止的安全位置。没有保证运行时自动检测安全位置的安全方法。如果Task
在请求取消后立即自动取消其自身,则可能会将其置于未知状态,因此应由开发人员标记这些安全位置。在您的Task
中分散检查取消的次数并不少见。
我刚注意到您的TriggerWeekChanged
函数是async void
。当它用于非事件处理程序的东西时,通常被认为是反模式。跟踪方法中async
操作的已完成状态,并处理可能从其中抛出的任何异常,都会导致很多问题。你应该非常厌倦标记为async void
并且不是事件处理程序的任何事情,因为做99%或更多时间是错误的。我强烈建议将其更改为async Task
,并考虑从其他代码传递CancellationToken
。
答案 1 :(得分:2)
您没有提及LoadDataForSelectedWeek
和Refresh
如何互动。
简而言之,您需要创建一个处理每次点击的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
}