可以在ExecuteReader中使用CancellationToken吗?

时间:2014-05-15 18:14:07

标签: c# .net

新的异步ExecuteReaderAsync采用CancellationToken。有没有办法取消旧的同步ExecuteReader

在我们的例子中,所有数据操作在后台线程上都是同步的,因此await不是一个选项。我不想开始第二个线程 - Task.Run(() => command.ExecuteReaderAsync(token)).Result似乎是浪费只是为了能够从UI线程取消。

2 个答案:

答案 0 :(得分:2)

与使用Begin或Async API及其线程池延续相比,性能测试显示使用专用同步数据读取线程几乎可获得2倍的性能优势。 (由于数秒钟内将加载数千万行,因此在这种情况下我们更喜欢性能。)

方便令牌传递的扩展方法:

public static SqlDataReader ExecuteReader(this SqlCommand command, CommandBehavior commandBehavior, CancellationToken cancellationToken)
{
    try
    {
        using (cancellationToken.Register(command.Cancel))
            return command.ExecuteReader(commandBehavior);
    }
    catch (SqlException) when (cancellationToken.IsCancellationRequested)
    {
        throw new OperationCanceledException(cancellationToken);
    }
}

我使用Reflector反编译进行了一些探索。 Begin和Async版本都非常节俭,但都完全基于TPL异步。因此,在两者上都有连续的线程池调度。

此扩展方法没有线程开销。在令牌源上调用Cancel的线程也将调用command.Cancel,这将立即在数据线程中导致SqlException

答案 1 :(得分:1)

我无权对您的编辑作出回应,但您应该了解一些关于初始问题的事项:

  1. Task.Run( ... ).Result正在封锁;这种语法有点误导。
  2. await Task.Run( () => command.ExecuteReaderAsync(token));将仅阻止执行方法的其余部分;允许将其余部分视为回调。
  3. await Task.Run( () => command.ExecuteReaderAsync(token), token);如上所述,但允许任务并行库也可以使用取消令牌。
  4. 关于主要问题,this msdn article表明ExecuteReaderAsync()真正尊重了cancelToken。请记住,框架中有几种方法实际上不会这样做。