我正在编写一种将单独的文本行异步写入文件的方法。如果它被取消,它会删除创建的文件并跳出循环。
这是运行良好的简化代码......我标记了 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;
}
1
我在 Close()FileStream 上调用 StreamWriter,我认为它会同步运行并阻塞线程。我该如何改进?我不想等待它将缓冲区刷新到文件中然后删除文件。
2
我想在 Dispose 范围的末尾将调用 DisposeAsync 方法而不是 using 。 (这个假设正确吗?)。
因此,Dispose会阻塞线程,并且为了防止我先用FlushAsync进行刷新,以便Dispose可以执行更少的操作。 (这在多大程度上是真的?)
我也可以删除 using,而是可以在这两个地方手动编写 DisposeAsync。但是会降低可读性。
如果我用 FileStream 打开 useAsync = true 会在 DisposeAsync 块结束时自动调用 using 吗?

对上述代码执行更好的任何解释或变体表示赞赏。

最佳答案

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

C# 8 带来了新的 await using 语法,但由于某种原因,What's new in C# 8.0 文章中没有提到它。

但它是 mentioned elsewhere

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

但还要注意,这仅在以下情况下有效:
  • 您正在使用 .NET Core 3.0+,因为那时引入了 IAsyncDisposable
  • 安装 Microsoft.Bcl.AsyncInterfaces NuGet 包。尽管这仅添加了接口(interface),并不包括使用它的 Stream 类型( FileStreamStreamWriter 等)的版本。

  • 即使在 Announcing .NET Core 3.0 的文章中,IAsyncDisposable 也只是顺便提及,从未展开。

    另一方面,您不需要这样做(我现在明白为什么了):
    writer.Close();
    stream.Close();
    

    the documentation for Close says:



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

    因此,如果您确实需要专门关闭文件,但又想异步执行,只需调用 DisposeAsync() 即可。它做同样的事情。
    await writer.DisposeAsync();
    
    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;
    }
    

    关于c# - 在C#流上使用DisposeAsync的正确方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58696427/

    10-10 01:28