2017-02-17 45 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

それが完了する前に操作がキャンセルされた場合、返されたタスクは、ステータスプロパティのキャンセル値が含まれています。

これはキャンセルトークンをキャンセルすると操作がキャンセルされることを意味します。しかし、それは事前にキャンセルされていない場合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が.ReadAsyncをオーバーライドしない[__ConsoleStream](https://referencesource.microsoft.com/#mscorlib/system/io/__consolestream.cs,de9f3a925342686c)のインスタンスを返していることに注意してください。 –

+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の実装に応じて、異なることをしなければならないからです。

編集:Stream抽象クラスは、非同期操作を扱うすべての抽象メソッドを持っていないため

はもう少しこのことについて考えると、それは、あなたがReadAsyncを取り消すことができないという意味になります。 Streamを実装するために必要な作業は、いくつかのゲッターとのブロックメソッドを実装することです。これはすべてMicrosoftが__ConsoleStreamクラスで行ったものです。

ストリームに存在することが保証できる唯一の方法はブロックメソッドであるため、ブロッキング呼び出しをキャンセルすることは不可能です(別のスレッドでブロックIO操作を行うことさえできません。操作が停止しています)、キャンセル可能な非同期操作を行うことは不可能です。

そこでマイクロソフトのいずれかをキャンセルトークンパラメータを削除している必要があります、または__ConsoleStreamをした人々がそれらを実装することを余儀なくされていたであろうように、Streamクラスに抽象非同期解約方法を入れている必要があります。

関連する問題