XmlWriter异步方法

问题描述 投票:9回答:3

我在msdn文档http://msdn.microsoft.com/en-us/library/system.xml.xmlwriter.aspx中找到了使用XmlWriter异步的示例

async Task TestWriter(Stream stream) 
{
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Async = true;
    using (XmlWriter writer = XmlWriter.Create(stream, settings)) {
        await writer.WriteStartElementAsync("pf", "root", "http://ns");
        await writer.WriteStartElementAsync(null, "sub", null);
        await writer.WriteAttributeStringAsync(null, "att", null, "val");
        await writer.WriteStringAsync("text");
        await writer.WriteEndElementAsync();
        await writer.WriteProcessingInstructionAsync("pName", "pValue");
        await writer.WriteCommentAsync("cValue");
        await writer.WriteCDataAsync("cdata value");
        await writer.WriteEndElementAsync();
        await writer.FlushAsync();
    }
}

我所知道的关于线程和异步编程的所有内容都告诉我,这是太慢的代码并且使用同步Write方法会快得多。我修改了这段代码并进行了测试。我发现我的正确和同步代码在文件超过100Mb时的速度提高3-4倍,在我的环境中低于10mb的文件速度提高8-10倍。

所以我的问题是,这种代码是否可用,并提供合理的性能提升?

c# .net async-await c#-5.0
3个回答
11
投票

首先,我不得不质疑基准测试。 100MB文件慢3-4倍是非常重要的。

但无论如何,async并不是要更快地做事。它是关于在该操作正在进行时执行其他操作。在客户端,您可以获得响应的好处;在服务器端,您可以获得可伸缩性的好处。

权衡是操作本身实际上更慢(但它应该稍慢,而不是慢3-4倍)。您可能没有使用真正的异步流进行写入(您必须异步打开文件流才能获得异步流)。


4
投票

在实施async/await时需要做出一些判断调用:即,您正在等待的操作可能包含足够的延迟,以至于async/await的小开销是值得的。作为Stephen points out,微软recommends 50 milliseconds作为经验法则的门槛。

在您的示例代码中,您的任何操作都不太可能超过50毫秒。

您的真实CDATA部分可能是异步/等待的有用候选者。在现实世界中,我们经常将base64编码的PDF写入XML流。在这样的示例中,您可能会发现等待代码的那部分是值得的。

async Task TestWriter(Stream stream, Stream sourceDocument)
{
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Async = true;
    using (XmlWriter writer = XmlWriter.Create(stream, settings))
    {
        writer.WriteStartElement("pf", "root", "http://ns");
        writer.WriteStartElement(null, "sub", null);
        writer.WriteAttributeString(null, "att", null, "val");
        writer.WriteString("text");
        writer.WriteEndElement();


        // Write the source document
        writer.WriteStartElement(null, "SourceDocument", null);
        Byte[] buffer = new Byte[4096];
        int bytesRead = await sourceDocument.ReadAsync(buffer, 0, 4096);
        while (bytesRead > 0)
        {
            await writer.WriteBase64Async(buffer, 0, bytesRead);
            bytesRead = await sourceDocument.ReadAsync(buffer, 0, 4096);
        }
        writer.WriteEndElement(); // SourceDocument

        writer.WriteEndElement(); // pf
        await writer.FlushAsync();
    }
}

当然,如果你在代码的任何部分咬掉async/await,你必须在你的整个调用堆栈中实现它以使它值得,包括像Stephen在他的评论中指出的用async标志打开文件这些细微点。


2
投票

我认为Stephen Cleary的答案是正确的,应该被接受为答案。但是,我只是想把我的观点放在这里,因为它可能太大而无法发表评论。

在我看来,async / await关键字,除了其他一切,还有两个显着的优点:

1)await是一个非阻塞等待 - 传统上,当我们启动一个异步任务(比如通过另一个线程)并希望它的结果/完成时,我们使用了Wait(),WaitAll(),Thread.Join()等(我们还使用了APM和EAP - 我将在第2点提到它。这些都是阻塞调用 - 这意味着它们会阻塞线程(如Thread.Sleep)。因此,如果UI线程阻塞,UI将冻结 - 如果太多ThreadPool线程阻塞,则ThreadPool必须承担创建新线程的开销。通过使用async / await - 我们可以执行非阻塞等待。在高级别,async / await关键字会将方法分解为状态机(例如,await之前和之后的委托集),并在异步任务结束时调度委托(使用TPL任务调度程序)。这样我们就可以在不冻结UI或阻止ThreadPool线程的情况下等待。因此,总结async / await将节省资源(线程)并且不会加速东西 - 它将拥有自己的状态机和调度开销。

2)async / await将代码可读性增加了数十倍 - 如果你使用过EAP或APM(它们在某种程度上是非阻塞的) - 那么你必须颠倒代码。很难弄清楚谁在调用谁 - 在哪里处理异常。对开发者来说是梦魇。使用async / await,我们可以编写看起来像同步的异步代码。

async / await有自己的方式来考虑异步代码并有自己的一套陷阱 - 因此,必须仔细使用它。

我认为MSDN上的示例仅用于API演示目的。

© www.soinside.com 2019 - 2024. All rights reserved.