取消执行并在方法重新进入时重新执行
本文关键字:执行 新执行 新进入 方法 取消 | 更新日期: 2023-09-27 17:50:20
我在这里看到过一个类似的问题,但它似乎不太适合我的场景。
我们有一个可以执行请求的UI,如果用户想再次执行请求(使用不同的查询参数),我们希望放弃初始请求,忽略它的响应,只使用最新的请求响应。
现在我有:
private readonly IDataService _dataService;
private readonly MainViewModel _mainViewModel;
private CancellationTokenSource _cancellationTokenSource;
//Constructor omitted for brevity
public async void Execute()
{
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
_cancellationTokenSource = new CancellationTokenSource();
try
{
string dataItem = await _dataService.GetDataAsync(_mainViewModel.Request, _cancellationTokenSource.Token);
_mainViewModel.Data.Add(dataItem);
}
catch (TaskCanceledException)
{
//Tidy up ** area of concern **
}
}
这似乎运行良好,我有一个很好的响应UI,但我有一个场景,我关心的:
- 用户发出请求
- 用户发出一个新的请求,取消原来的请求
- 在原始被取消的请求抛出异常,用当前所需的数据填充UI之前,新请求返回
- 抛出异常并进行清理,覆盖新请求输出
这可能非常罕见,但我认为这是一种可能性,除非我的理解是错误的。
是否有任何方法可以确保如果一个任务通过取消令牌请求被取消,并且一个新的任务被启动,取消发生在新任务启动/返回执行之前,而不阻塞UI线程?
是否有办法确保如果一个任务被取消取消令牌请求,并启动一个新的任务在新任务启动/返回执行之前发生阻塞UI线程?
任何能扩展我对这个问题的理解的阅读都将是最重要的感激。
首先,一些相关的阅读和问题,根据要求:
- 《异步重入,以及处理它的模式》作者:Lucian Wischik
- 如何避免重入与异步void事件处理程序?
- 任务排序和重入
- 正确取消异步操作并再次启动
- 同步取消UI线程上的挂起任务
- 如何优雅地取消异步工作?
如果我正确地理解了你的问题,你主要关心的是,同一任务的先前的实例一旦完成,可能会用过时的结果更新UI(或ViewModel
)。
为了确保这不会发生,在之前使用ThrowIfCancellationRequested
和相应的令牌,你要更新UI/模型,在你做的地方。那么,如果最近的任务实例在前一个较旧的任务实例之前完成,就无关紧要了。旧的任务将在之前到达ThrowIfCancellationRequested
点,它可能有机会做任何有害的事情,所以您不必await
旧的任务:
public async void Execute()
{
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
_cancellationTokenSource = new CancellationTokenSource();
var token = _cancellationTokenSource.Token.
try
{
string dataItem = await _dataService.GetDataAsync(
_mainViewModel.Request,
token);
token.ThrowIfCancellationRequested();
_mainViewModel.Data.Add(dataItem);
}
catch (OperationCanceledException)
{
//Tidy up ** area of concern **
}
}
另一个问题是,当前面的任务失败时,除了TaskCanceledException
,该怎么办。这也可能发生在新任务完成之后。您可以决定是否忽略这个异常,重新抛出它,或者做其他事情:
try
{
string dataItem = await _dataService.GetDataAsync(
_mainViewModel.Request,
token);
token.ThrowIfCancellationRequested();
_mainViewModel.Data.Add(dataItem);
}
catch (Exception ex)
{
if (ex is OperationCanceledException)
return
if (!token.IsCancellationRequested)
{
// thrown before the cancellation has been requested,
// report and re-throw
MessageBox.Show(ex.Message);
throw;
}
// otherwise, log and ignore
}