12

我在 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();
    }
}

我对线程和异步编程的所有了解都告诉我,这是太慢的代码,使用同步写入方法会快得多。我已经修改了这段代码并对其进行了测试。我发现我是对的,并且在我的环境中,对超过 100Mb 的文件的同步代码速度提高了 3-4 倍,在小于 10mb 的文件上同步代码的速度提高了 8-10 倍以上。

所以我的问题是有没有这样的代码可用并提供合理的性能提升的情况?

4

3 回答 3

11

首先,我必须质疑基准。在 100MB 文件上慢 3-4 倍真的很重要。

但不管怎样,async都不是为了更快地做事。这是关于在该操作进行时做其他事情。在客户端,您将受益于响应能力;在服务器端,您可以获得可扩展性的好处。

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

于 2013-05-20T01:47:26.137 回答
5

在实现时需要做出一些判断async/await:即,您正在等待的操作是否可能包含足够的延迟,以至于小开销async/await是值得的。正如斯蒂芬指出的那样,微软建议将 50 毫秒作为经验阈值。

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

您的实际 CDATA 部分可能是 async/await 的值得候选者。在现实世界中,我们经常将 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代码的任何部分,则必须在整个调用堆栈中实现它以使其值得,包括像async斯蒂芬在他的评论中指出的那样打开带有标志的文件之类的微妙点。

于 2016-05-23T12:39:29.707 回答
2

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

在我看来,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 线程的情况下等待。所以,

2) async/await 将代码的可读性提高了无数倍——如果你使用过 EAP 或 APM(它们在某种程度上是非阻塞的)——那么你必须把你的代码颠倒过来。很难弄清楚谁在打电话给谁——在哪里处理异常。开发商的噩梦。使用 async/await,我们可以编写看起来像同步的异步代码。

async/await 有自己的方式来看待异步代码,并且有自己的一套陷阱——因此,必须小心使用它。

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

于 2013-05-24T23:11:42.683 回答