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