我可以使用CancellationToken取消StreamReader.ReadLineAsync吗?

H W*_*H W 18 .net c# asynchronous cancellation-token

当我取消,内容如下异步方法调用Cancel()我的方法CancellationTokenSource,它最终会停下来.但是由于该行Console.WriteLine(await reader.ReadLineAsync());需要相当多的时间来完成,我还尝试将CancellationToken传递给ReadLineAsync()它(期望它返回一个空字符串),以使该方法对我的Cancel()调用更具响应性.但是我不能错过一个CancellationTokenReadLineAsync().

我可以取消打电话给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());
    }
}
Run Code Online (Sandbox Code Playgroud)

更新 如下面的评论中所述,Console.WriteLine()由于每行40.000个字符的输入字符串格式不正确,单独调用已占用几秒钟.打破这个问题可以解决我的响应时间问题,但是我仍然对如何取消这个长时间运行的语句的任何建议或解决方法感兴趣,如果由于某种原因打算将40.000个字符写入一行(例如将整个字符串转储到一份文件).

Mar*_*cka 14

.NET 6 带来了Task.WaitAsync(CancellationToken)。所以可以这样写:

using StreamReader reader = new StreamReader(dataStream);
while (!reader.EndOfStream)
{       
    Console.WriteLine(await reader.ReadLineAsync().WaitAsync(cancellationToken).ConfigureAwait(false));
}
Run Code Online (Sandbox Code Playgroud)

在.NET 7(尚未发布)中,应该可以简单地编写:

using StreamReader reader = new StreamReader(dataStream);
while (!reader.EndOfStream)
{       
    Console.WriteLine(await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)

基于https://github.com/dotnet/runtime/issues/20824https://github.com/dotnet/runtime/pull/61898


i3a*_*non 9

除非可取消,否则您无法取消操作.您可以使用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);
}
Run Code Online (Sandbox Code Playgroud)

用法:

await task.WithCancellation(cancellationToken);
Run Code Online (Sandbox Code Playgroud)

你不能取消Console.WriteLine,你不需要.如果你有一个合理的尺寸,它是瞬间的string.

关于指南:如果您的实现实际上不支持取消,则不应接受令牌,因为它发送混合消息.

如果你有一个巨大的字符串写入控制台,你不应该使用Console.WriteLine.您可以一次在字符中写入字符串,并且可以取消该方法:

public void DumpHugeString(string line, CancellationToken token)
{
    foreach (var character in line)
    {
        token.ThrowIfCancellationRequested();
        Console.Write(character);
    }

    Console.WriteLine();
}
Run Code Online (Sandbox Code Playgroud)

更好的解决方案是批量写入而不是单个字符.这是一个使用MoreLinq's 的实现Batch:

public void DumpHugeString(string line, CancellationToken token)
{
    foreach (var characterBatch in line.Batch(100))
    {
        token.ThrowIfCancellationRequested();
        Console.Write(characterBatch.ToArray());
    }

    Console.WriteLine();
}
Run Code Online (Sandbox Code Playgroud)

所以,最后:

var reader = new StreamReader(dataStream);
while (!reader.EndOfStream)
{
    DumpHugeString(await reader.ReadLineAsync().WithCancellation(token), token);
}
Run Code Online (Sandbox Code Playgroud)

  • 你确定 `Task.ContinueWith` 也会取消整个任务吗?在我看来 `Task.ContinueWith` 不适合这里。你需要类似`Task.WhenAny(task,infiniteCancellableTaks)`的东西,其中`varinfiniteCancellableTaks = Task.Delay(Timeout.Infinite, token)`,就像在这个例子中http://stackoverflow.com/a/23473779/2528649 (2认同)