是否可以在ExecuteReader中使用CancellationToken ?
本文关键字:CancellationToken ExecuteReader 是否 | 更新日期: 2023-09-27 18:03:30
新的异步ExecuteReaderAsync
接受一个CancellationToken。有没有办法取消旧的同步ExecuteReader
?
在我们的例子中,所有的数据操作在一个后台线程上是同步的,所以await
不是一个选项。我不想启动第二个线程- Task.Run(() => command.ExecuteReaderAsync(token)).Result
似乎只是为了能够从UI线程取消浪费。
性能测试显示,与使用Begin或Async api及其线程池延续相比,使用专用同步数据读取线程的性能优势几乎是前者的两倍。(由于数以千万计的行将在几秒钟内加载,因此在这种情况下我们更喜欢性能。)
方便令牌传递的扩展方法:
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
。
我无法权威地回应你的编辑,但关于你最初的问题,有几件事你应该知道:
-
Task.Run( ... ).Result
正在阻塞;这个语法有点误导人。 -
await Task.Run( () => command.ExecuteReaderAsync(token));
将只阻塞执行方法的剩余部分; -
await Task.Run( () => command.ExecuteReaderAsync(token), token);
的工作方式与上面一样,但也允许任务并行库尊重您的取消令牌。
关于主要问题,这篇msdn文章建议ExecuteReaderAsync()确实尊重那个cancellationToken。请记住,框架中有几个方法实际上不会这样做。