Question

The new async ExecuteReaderAsync takes a CancellationToken. Is there any way to cancel the old synchronous ExecuteReader?

In our case, all the data operations are synchronous on a background thread, so await is not an option. I don't want to start a second thread- Task.Run(() => command.ExecuteReaderAsync(token)).Result seems a waste just to be able to cancel from the UI thread.

Was it helpful?

Solution

Performance tests showed almost 2x performance benefit to a using a dedicated synchronous data-reading thread, compared to using the Begin or Async APIs with their thread pool continuations. (Since tens of millions of rows will be loaded in as many seconds, we prefer performance in this case.)

An extension method for convenient token passing:

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);
    }
}

I did some spelunking with Reflector decompilation. The Begin and Async versions are both very frugal, but are both fully based on the TPL async. Because of that, there is thread pool dispatching for continuations on both.

This extension method has no threading overhead. The thread that calls Cancel on the token source will also call command.Cancel which will immediately cause a SqlException in the data thread.

OTHER TIPS

I can't respond authoritatively to your edit but there's a few things you should know about your initial question:

  1. Task.Run( ... ).Result is blocking; that syntax is a little misleading.
  2. await Task.Run( () => command.ExecuteReaderAsync(token)); will block only the remainder of the executing method; allowing the rest to be treated as a callback.
  3. await Task.Run( () => command.ExecuteReaderAsync(token), token); works as above but allows the task parallel library to honor to your cancellation token as well.

As to the main question, this msdn article suggests that ExecuteReaderAsync() is truly honoring that cancellationToken. Bear in mind there are a couple of methods in the framework that will not actually do that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top