如何以正确的方式取消异步查询
Posted
技术标签:
【中文标题】如何以正确的方式取消异步查询【英文标题】:How to cancel an async query the right way 【发布时间】:2013-08-07 09:57:55 【问题描述】:这是this question 的后续问题。
我正在尝试从我的数据库中加载数据,这需要 5-10 秒,但我希望 GUI 保持响应,并且它应该可以取消。
private CancellationTokenSource _source;
public IEnumerable<Measurement> Measurements get ... set ...
private async void LoadData()
_source = new CancellationTokenSource();
using (var context = new TraceContext())
Measurements = null;
Measurements = await context.Measurements.ToListAsync(_source.Token);
private void Cancel()
if (_source != null)
_source.Cancel();
public RelayCommand ReloadCommand
get return _reloadCommand ?? (_reloadCommand = new RelayCommand(Reload));
private RelayCommand _reloadCommand;
public RelayCommand CancelCommand
get return _cancelCommand ?? (_cancelCommand = new RelayCommand(Cancel));
private RelayCommand _cancelCommand;
我已经尝试了一些事情,但我无法让它正常工作,这只是加载列表,仅此而已,我无法取消它。
这其中的错误在哪里?
【问题讨论】:
当您说“我无法取消”时,当您告诉CancellationTokenSource
取消时,实际会发生什么?
什么都没有发生,据我所知,如果仍有使用此令牌运行的任务,它应该抛出异常,但这不会发生。
什么是ToListAsync
?通过一些谷歌搜索,我找不到它作为 MSDN 或其他东西的一部分。也许你写了它并且它包含一个错误,所以令牌没有被正确应用?
这不是取消的工作方式。仅当尝试对任务进行Wait()
或观察任务的结果时,已取消的任务(已转换为Cancelled
状态的Task
)引发异常。在这种情况下,任务由 EF 控制,并且只有在 EF 选择时才会转换到该状态。在不知道他们的内部细节的情况下,测试版可能不会这样做。
@TimS。这是新的 EF6 功能的一部分 msdn.microsoft.com/en-us/data/jj819165.aspx 编辑:goo.gl/kMR5D 可能是更好的来源
【参考方案1】:
感谢您提出这个问题。目前,EF 中此异步 API 的实现依赖于底层 ADO.NET 提供程序来支持取消,但 SqlDataReader.ReadAsync 有一些限制,我们观察到在许多情况下,当请求取消时它不会立即取消。我们正在考虑在 EF6 RTM 中修复 a bug,这是关于引入我们自己的检查,以检查 EF 方法内的行读取之间的取消请求。
与此同时,您可以通过使用 ForEachAsync() 将项目添加到列表并检查每一行来解决此限制,例如(未经彻底测试):
public async static Task<List<T>> MyToListAsync<T>(
this IQueryable<T> source,
CancellationToken token)
token.ThrowIfCancellationRequested();
var list = new List<T>();
await source.ForEachAsync(item =>
list.Add(item);
token.ThrowIfCancellationRequested();
);
return list;
【讨论】:
我现在已经尝试过了,它可以工作,但只有在加载数据之后(用wireshark检查所有数据都加载了,但列表的填充被取消了)。另外我认为ForEachAsync
也应该用CancellationToken
=> source.ForEachAsync(..., token)
调用,但它看起来像ForEachAsync
也没有以正确的方式实现取消模式,因为数据仍在加载并且它不会' t 抛出异常。
我提到的错误是关于在 ForEachAsync 中添加取消请求检查,因此当我们修复错误时应该解决这部分问题。另一个有趣的方面是,由于在 EF6 中我们默认缓冲查询结果,数据读取器的加载(即对 ReadAsyc 的调用)在我们开始具体化结果之前发生在下一级。我会将这个细节添加到错误中,以便我们在修复错误时考虑它。
顺便说一下,为了避免加载完整的结果,您可以将 .AsStreaming() 添加到查询中。这将产生其他后果,例如流式查询与连接弹性功能不兼容。以上是关于如何以正确的方式取消异步查询的主要内容,如果未能解决你的问题,请参考以下文章