在C#流上使用DisposeAsync的正确方法

Biz*_*han 3 c# asynchronous stream

我正在写一个异步将单独的文本行写入文件的方法。如果取消,它将删除创建的文件并跳出循环。

这是简化的代码,可以正常工作。。。我标记了2点,我不确定它们是如何处理的。我希望代码在任何情况下都不会阻塞线程。

public async Task<IErrorResult> WriteToFileAsync(string filePath,
                                                 CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();

    using var stream = new FileStream(filePath, FileMode.Create);
    using var writer = new StreamWriter(stream, Encoding.UTF8);

    foreach (var line in Lines)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            //
            // [1] close, delete and throw if cancelled
            //
            writer.Close();
            stream.Close();
            if (File.Exists(filePath))
                File.Delete(filePath);
            throw new OperationCanceledException();
        }

        // write to the stream
        await writer.WriteLineAsync(line.ToString());
    }

    //
    // [2] flush and let them dispose
    //
    await writer.FlushAsync();
    await stream.FlushAsync();
    // await stream.DisposeAsync();
    return null;
}
Run Code Online (Sandbox Code Playgroud)

1个

我打电话Close()FileStreamStreamWriter我认为这将同步运行,并阻止该线程。我该如何改善?我不想等待它将缓冲区刷新到文件中,然后删除文件。

2

我想Dispose将调用该方法,而不是DisposeAsyncusing范围的末尾。(此假设正确吗?)。

因此,请Dispose阻塞线程,以防止我先进行冲洗,FlushAsync减少Dispose执行任务。(这在多大程度上是真的?

我也可以删除using,而可以DisposeAsync在这两个地方手动编写。但这会降低可读性。

如果我打开FileStreamwith useAsync = true,它将DisposeAsyncusing块结束时自动调用?


以上代码的任何解释或变型表现得更好的表示赞赏。

Gab*_*uci 6

如您所愿,该using语句将调用Dispose(),而不是DisposeAsync()

C#8带来了新的await using语法,但是由于某些原因,C#8.0的新增功能一文中未提及。

但这是在其他地方提到的

await using var stream = new FileStream(filePath, FileMode.Create);
await using var writer = new StreamWriter(stream, Encoding.UTF8);
Run Code Online (Sandbox Code Playgroud)

但也请注意,这仅在以下情况下有效:

  • 您使用的是.NET Core 3.0+,因为它是在IAsyncDisposable最初引入的,或者
  • 安装Microsoft.Bcl.AsyncInterfaces NuGet程序包。虽然这只是增加了接口和不包括的版本Stream类型(FileStreamStreamWriter,等)使用它。

即使在发布.NET Core 3.0的文章中,IAsyncDisposable也只是顺便提及而从未对其进行扩展。

另一方面,您不需要执行此操作(我现在知道为什么):

writer.Close();
stream.Close();
Run Code Online (Sandbox Code Playgroud)

由于文档中Close说:

此方法调用Dispose,指定true释放所有资源。您不必专门调用Close方法。相反,请确保正确处理每个Stream对象。

由于您使用usingDispose()(或DisposeAsync())将被自动调用,并且Close不会做任何尚未发生的事情。

因此,如果您确实需要专门关闭文件,但又想异步完成,则只需调用即可DisposeAsync()。它做同样的事情。

await writer.DisposeAsync();
Run Code Online (Sandbox Code Playgroud)
public async Task<IErrorResult> WriteToFileAsync(string filePath,
                                                 CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();

    await using var stream = new FileStream(filePath, FileMode.Create);
    await using var writer = new StreamWriter(stream, Encoding.UTF8);

    foreach (var line in Lines)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            // not possible to discard, FlushAsync is covered in DisposeAsync
            await writer.DisposeAsync(); // use DisposeAsync instead of Close to not block

            if (File.Exists(filePath))
                File.Delete(filePath);
            throw new OperationCanceledException();
        }

        // write to the stream
        await writer.WriteLineAsync(line.ToString());
    }

    // FlushAsync is covered in DisposeAsync
    return null;
}
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一下,[DisposeAsync()将刷新缓冲区](https://github.com/dotnet/corefx/blob/ac99a1b7168bd32046a954c3f06012c0fa909bed/src/Common/src/CoreLib/System/IO/FileStream.Windows.cs# L278),因此您无需分别调用`FlushAsync()`。 (2认同)