2017-02-17 1 views
0

Почему код, приведенный ниже, не заполняется, если вы не вводите какой-либо ввод, и почему он все еще реагирует на нажатие клавиши даже после отмены маркера отмены?Как отменить 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 

The documentation for Stream.ReadAsync says:

Если операция будет отменена до ее завершения, возвращаемая задача содержит Отменено значения для свойства Status.

Это означает, что отмена маркера отмены отменяет операцию, верно? Тем не менее, по какой-то причине the source code for Stream.ReadAsync ничего с маркером отмены не делать, если он не отменен заранее:

public virtual Task<int> 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<int>(cancellationToken) 
       : BeginEndReadAsync(buffer, offset, count); 
} 

Поэтому лексема параметр отмены бессмысленно - как я могу отменить, что асинхронная читать?

+0

Обратите внимание, что Console.OpenStandardInput возвращается экземпляр [__ConsoleStream] (https://referencesource.microsoft.com/#mscorlib/system/io/__consolestream.cs,de9f3a925342686c), который не переопределяет .ReadAsync –

+0

В следующий раз прежде чем вы сделаете ненужную магию, чтобы отменить токен через некоторое время, используйте соответствующий конструктор: 'var cancelationSource = new CancellationTokenSource (TimeSpan.FromSeconds (2));'. https://msdn.microsoft.com/en-us/library/hh139229(v=vs.110).aspx ' –

+0

@PeterBons Для меня мой способ более с готовностью сообщает, что что-то будет распечатано на консоли после –

ответ

0

В частном случае ввода консоли, кажется, нет никакого другого пути, чем опрашивать 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 вы имеете дело.

Edit:

Думая об этом немного больше, это имеет смысл, что вы не можете отменить ReadAsync, потому что Stream абстрактный класс не имеет каких-либо абстрактные методы, касающиеся асинхронных операций; все, что вы должны сделать для реализации Stream, реализует некоторые getters и некоторые , блокирующие методы, которые все Microsoft выполнила с классом __ConsoleStream.

Поскольку единственными методами, которые могут быть гарантированы для существования в потоке, являются методы блокировки, и поскольку невозможно отменить вызов блокировки (вы даже не можете блокировать операцию ввода-вывода в другом потоке, отменить поток и иметь останов операции), невозможно иметь отменяемые асинхронные операции.

Поэтому Microsoft либо должна была удалить параметр маркера отмены, либо должна была поместить абстрактные асинхронные методы отмены в класс Stream, чтобы люди, сделавшие __ConsoleStream, были вынуждены их реализовать.