我看到有人问类似的问题 here但这似乎不太适合我的场景。
我们有一个可以执行请求的 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
- 抛出异常并进行清理,覆盖新的请求输出
这可能非常罕见,但我认为这是一种可能性,除非我对此的理解是错误的。
是否有任何方法可以确保,如果通过取消 token 请求取消任务并启动新任务,则取消会在新任务启动/返回执行之前发生,而不会阻塞 UI 线程?
任何能够扩展我对此的理解的阅读都将不胜感激。
最佳答案
Is there any way to ensure that if a Task is cancelled via a cancellation token request and a new Task is started the cancellation happens before the new task is started/returns execution without blocking the UI thread?
Any reading to expand my understanding on this would be most appreciated.
首先,根据要求提供一些相关的阅读 Material 和问题:
- "Async re-entrancy, and the patterns to deal with it"作者:卢西恩·维希克
- How to avoid reentrancy with async void event handlers?
- Task sequencing and re-entracy
- Correctly cancel async operation and fire it again
- Cancelling a pending task synchronously on the UI thread
- How to cancel async work gracefully?
如果我正确理解您的问题,您主要担心的是,同一任务的前一个实例可能会使用过时的结果更新 UI(或 ViewModel
),一旦它已完成。
为了确保这种情况不会发生,请在您要更新 UI/模型之前,在各处使用 ThrowIfCancellationRequested
和相应的 token 。 > 你就这么做。那么,任务的最新实例是否先于上一个较旧的实例完成就无关紧要了。较旧的任务将在它可能有机会执行任何有害操作之前到达 ThrowIfCancellationRequested
点,因此您不必等待
较旧的任务任务:
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
}
关于c# - 取消执行并在方法重新进入时重新执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22425321/