【问题标题】: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 可能是更好的来源

标签: c# .net wpf entity-framework entity-framework-6


【解决方案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() 添加到查询中。这将产生其他后果,例如流式查询与连接弹性功能不兼容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-17
  • 2018-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多