新的异步ExecuteReaderAsync
采用CancellationToken。有没有办法取消旧的同步ExecuteReader
?
在我们的例子中,所有数据操作在后台线程上都是同步的,因此await
不是一个选项。我不想开始第二个线程 - Task.Run(() => command.ExecuteReaderAsync(token)).Result
似乎是浪费只是为了能够从UI线程取消。
答案 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)
我无权对您的编辑作出回应,但您应该了解一些关于初始问题的事项:
Task.Run( ... ).Result
正在封锁;这种语法有点误导。await Task.Run( () => command.ExecuteReaderAsync(token));
将仅阻止执行方法的其余部分;允许将其余部分视为回调。await Task.Run( () => command.ExecuteReaderAsync(token), token);
如上所述,但允许任务并行库也可以使用取消令牌。关于主要问题,this msdn article表明ExecuteReaderAsync()真正尊重了cancelToken。请记住,框架中有几种方法实际上不会这样做。