如何取消Stream.ReadAsync?

如果您没有输入任何输入,为什么下面的代码没有完成?为什么即使在取消令牌被取消后它仍然响应按下的键?

// Set up a cancellation token var cancellationSource = new CancellationTokenSource(); // Cancel the cancellation token after a little bit of time Task.Run(async () => { await Task.Delay(TimeSpan.FromSeconds(2)); cancellationSource.Cancel(); Console.WriteLine("Canceled the cancellation token"); }); // Wait for user input, or the cancellation token Task.Run(async () => { try { using (var input = Console.OpenStandardInput()) { var buffer = new byte[1]; Console.WriteLine("Waiting for input"); await input.ReadAsync(buffer, 0, 1, cancellationSource.Token); // This is impossible to cancel??? Console.WriteLine("Done waiting for input"); // This never happens until you press a key, regardless of the cancellation token } } catch (Exception e) { Console.WriteLine(e.Message); // No errors } }) .Wait(); // Block until complete 

Stream.ReadAsync的文档说 :

如果操作在完成之前取消,则返回的任务包含Status属性的Canceled值。

这意味着取消取消令牌将取消操作,对吧? 但由于某种原因,Stream.ReadAsync的源代码如果未事先取消,则不会对取消令牌执行任何操作:

 public virtual Task ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { // If cancellation was requested, bail early with an already completed task. // Otherwise, return a task that represents the Begin/End methods. return cancellationToken.IsCancellationRequested ? Task.FromCancellation(cancellationToken) : BeginEndReadAsync(buffer, offset, count); } 

因此,取消令牌参数毫无意义 – 如何取消该异步读取?

在Console输入的特定情况下,似乎没有其他方法可以轮询Console.KeyAvailable属性:

 var buffer = new byte[1]; Console.WriteLine("Waiting for input"); while (!Console.KeyAvailable && !cancellationSource.Token.IsCancellationRequested) await Task.Delay(10); // You can add the cancellation token as a second parameter here, but then canceling it will cause .Delay to throw an exception if (cancellationSource.Token.IsCancellationRequested) { Console.WriteLine("Canceled; no longer waiting for input"); } else { await input.ReadAsync(buffer, 0, 1); Console.WriteLine("Got user input"); } 

对我而言,这表明您无法以一般方式可靠地使用Stream.ReadAsync ,因为您必须根据您正在处理的Stream实现执行不同的操作。

编辑:

考虑到这一点,有意义的是你不能取消ReadAsync ,因为Stream抽象类没有任何处理异步操作的抽象方法; 实现Stream要做的就是实现一些getter和一些阻塞方法,这些都是Microsoft使用__ConsoleStream类完成的。

因为可以保证在Stream上存在的唯一方法是阻塞方法,并且因为不可能取消阻塞调用(你甚至不能在另一个线程上执行阻塞IO操作,取消线程,并让操作停止),不可能有可取消的异步操作。

因此,Microsoft应该已经删除了取消令牌参数,或者应该将抽象的异步可取消方法放入Stream类中,以便强制实现__ConsoleStream的人们实现它们。

Interesting Posts