CancellationTokenSource
のCancel()
メソッドを呼び出して、次の内容の非同期メソッドをキャンセルすると、最終的に停止します。ただし、行Console.WriteLine(await reader.ReadLineAsync());
の完了にはかなりの時間がかかるため、メソッドをさらに作成するために、CancellationTokenをReadLineAsync()
にも渡そうとしました(空の文字列を返すことを期待しています)。 Cancel()
呼び出しに応答します。ただし、CancellationToken
をReadLineAsync()
に渡すことができませんでした。
Console.WriteLine()
またはStreamreader.ReadLineAsync()
の呼び出しをキャンセルできますか?キャンセルする場合はどうすればよいですか?
ReadLineAsync()
がCancellationToken
を受け入れないのはなぜですか?メソッドがキャンセルされた後も完了している場合でも、AsyncメソッドにオプションのCancellationToken
パラメーターを指定することをお勧めします。
_StreamReader reader = new StreamReader(dataStream);
while (!reader.EndOfStream)
{
if (ct.IsCancellationRequested){
ct.ThrowIfCancellationRequested();
break;
}
else
{
Console.WriteLine(await reader.ReadLineAsync());
}
}
_
更新以下のコメントに記載されているように、Console.WriteLine()
呼び出しだけでも、1行あたり40.000文字の入力文字列の形式が不適切なため、すでに数秒かかっていました。これを分解すると応答時間の問題は解決しますが、何らかの理由で1行に40.000文字を書き込むことが意図されている場合(たとえば、文字列全体をにダンプする場合)、この長時間実行されるステートメントをキャンセルする方法に関する提案や回避策に関心があります。ファイル)。
キャンセルできない限り、操作をキャンセルすることはできません。 WithCancellation
拡張メソッドを使用して、コードフローをキャンセルされたかのように動作させることができますが、基になるコードは引き続き実行されます。
public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
return task.IsCompleted // fast-path optimization
? task
: task.ContinueWith(
completedTask => completedTask.GetAwaiter().GetResult(),
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
使用法:
await task.WithCancellation(cancellationToken);
Console.WriteLine
をキャンセルすることはできず、キャンセルする必要はありません。妥当なサイズのstring
があれば、それは瞬時です。
ガイドラインについて:実装が実際にキャンセルをサポートしていない場合、トークンは混合メッセージを送信するため、トークンを受け入れるべきではありません。
コンソールに書き込むための巨大な文字列がある場合は、Console.WriteLine
を使用しないでください。一度に1文字ずつ文字列を記述し、そのメソッドをキャンセル可能にすることができます。
public void DumpHugeString(string line, CancellationToken token)
{
foreach (var character in line)
{
token.ThrowIfCancellationRequested();
Console.Write(character);
}
Console.WriteLine();
}
さらに良い解決策は、単一の文字ではなくバッチで書き込むことです。 MoreLinq
のBatch
を使用した実装は次のとおりです。
public void DumpHugeString(string line, CancellationToken token)
{
foreach (var characterBatch in line.Batch(100))
{
token.ThrowIfCancellationRequested();
Console.Write(characterBatch.ToArray());
}
Console.WriteLine();
}
したがって、結論として:
var reader = new StreamReader(dataStream);
while (!reader.EndOfStream)
{
DumpHugeString(await reader.ReadLineAsync().WithCancellation(token), token);
}
私はこれを一般化しました answer これに:
public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken, Action action, bool useSynchronizationContext = true)
{
using (cancellationToken.Register(action, useSynchronizationContext))
{
try
{
return await task;
}
catch (Exception ex)
{
if (cancellationToken.IsCancellationRequested)
{
// the Exception will be available as Exception.InnerException
throw new OperationCanceledException(ex.Message, ex, cancellationToken);
}
// cancellation hasn't been requested, rethrow the original Exception
throw;
}
}
}
これで、キャンセル可能な非同期メソッドでキャンセルトークンを使用できます。例:WebRequest.GetResponseAsync:
var request = (HttpWebRequest)WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
{
. . .
}
となります:
var request = (HttpWebRequest)WebRequest.Create(url);
using (WebResponse response = await request.GetResponseAsync().WithCancellation(CancellationToken.None, request.Abort, true))
{
. . .
}
例を参照してください http://Pastebin.com/KauKE0rW
私は無限の遅延を使用するのが好きです、コードはかなりきれいです。 waiting
が完了すると、WhenAny
が返され、cancellationToken
がスローされます。それ以外の場合は、task
の結果が返されます。
public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
using (var delayCTS = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
{
var waiting = Task.Delay(-1, delayCTS.Token);
var doing = task;
await Task.WhenAny(waiting, doing);
delayCTS.Cancel();
cancellationToken.ThrowIfCancellationRequested();
return await doing;
}
}