【问题标题】:XElement to file save with async/await in C#XElement 在 C# 中使用 async/await 保存文件
【发布时间】:2012-09-10 23:30:33
【问题描述】:

我尝试使用异步/等待功能从 XElement 对象写入 xml 文件。 但我意识到 XElement.Save() 不能与 async/await 一起运行。

也许解决方案可以将 XElement.Save(Stream) 与 FileStream 对象一起使用...

所以,我写了一些代码如下,但很难用文件流处理。

public async Task SaveAsync(XElement xml, string filename)
{
    using (var fs = new FileStream(filename, FileMode.Create))
    {
        xml.Save(fs);
        await fs.WriteAsync(**please_help_me**);
    }
}

如何使用这种方法或有其他解决方案吗?

【问题讨论】:

    标签: c# filestream xelement async-await c#-5.0


    【解决方案1】:

    XElement 没有任何异步写入自身的方法,因此您使用它进行的任何调用都将是同步的。如果您需要使此方法异步(例如,这发生在 UI 线程中,并且您希望在后台线程上执行此操作,以便在保存 XML 时应用程序不会出现“冻结”),您可能想开始一个新任务,并将其保存在后台。

    public async Task SaveAsync(XElement xml, string filename)
    {
        await Task.Factory.StartNew(delegate
        {
            using (var fs = new FileStream("myFile", FileMode.Create))
            {
                xml.Save(fs);
            }
        });
    }
    

    【讨论】:

    • 是的,我知道。这就是为什么我尝试将 FileStream.WriteAsync 与 XElement.Save 方法交替使用的原因。这种方法是否也会阻塞 UI?
    • XElement.Save的调用是同步的,所以会阻塞。我用如何在后台线程上保存的示例更新了我的答案。
    • 这可以写成一行:await Task.Factory.StartNew(() => xml.Save(filename));
    • @codesparkle 来打代码高尔夫吧:await Task.Run(() => xml.Save(filename));.
    • @svick 从来没有使用过 Tasks,我没想到 ;) 是时候赶上 .NET4.5 了,看来。
    【解决方案2】:

    如果您真的不想阻塞 IO(并挂起 ThreadPool 线程/您的线程),您可能需要使用临时 MemoryStream:

    using(var ms = new MemoryStream())
    using(var fs = new FileStream("myFile", FileMode.Create))
    {
        xml.Save(ms);
        ms.Position = 0;
        await ms.CopyToAsync(fs)
    }
    

    【讨论】:

      【解决方案3】:

      截至.NET Core 2.0.NET Standard 2.1XElementXDocument 都具有SaveAsync() 方法。因此,您现在可以这样做:

      public static async Task SaveAsync(this XElement xml, string filename, SaveOptions options = default, CancellationToken cancellationToken = default)
      {
          // "await using" introduced in c# 8 / .NET Core 3.0+
          await using var stream =
              new FileStream(
              filename, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, 
              useAsync: true);
      
          await xml.SaveAsync(stream, options, cancellationToken);
      }
      

      注意事项:

      • 在 .NET Core 3 中,XDocument.SaveAsync()(可能还有XElement.SaveAsync())仍然包含至少一个阻塞调用。见XDocument.SaveAsync has a blocking call #29464。该问题应在.NET 5 中修复。

      • 根据docsuseAsync 参数到FileStream 构造函数

        指定是使用异步 I/O 还是同步 I/O。但是请注意,底层操作系统可能不支持异步 I/O,因此当指定 true 时,句柄可能会根据平台同步打开。当异步打开时,BeginRead(Byte[], Int32, Int32, AsyncCallback, Object)BeginWrite(Byte[], Int32, Int32, AsyncCallback, Object) 方法在大读取或写入时性能更好,但对于小读取或写入它们可能要慢得多。如果应用程序旨在利用异步 I/O,请将 useAsync 参数设置为 true。正确使用异步 I/O 可以将应用程序加速多达 10 倍,但使用它而不为异步 I/O 重新设计应用程序可能会降低多达 10 倍的性能。

        既然您知道要异步编写 XElement,那么您可能已经为异步 I/O 设计了您的应用程序。但是,您可能希望在使用和不使用异步 IO 的情况下进行基准测试,以确认您没有损害性能。

      演示小提琴here.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-06-08
        • 2023-03-31
        • 2019-05-30
        • 1970-01-01
        • 1970-01-01
        • 2021-09-07
        • 2016-01-15
        相关资源
        最近更新 更多