我尝试使用async / await功能从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**);
}
}
如何处理这种方法还是有其他解决方案?
答案 0 :(得分:5)
如果你真的不想阻止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)
}
答案 1 :(得分:2)
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);
}
});
}
答案 2 :(得分:1)
从.NET Core 2.0和.NET Standard 2.1开始,XElement
和XDocument
都有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中解决。
根据docs,useAsync
构造函数的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。