XDocument.Load 的多线程

Multithreading with XDocument.Load

我试图让我的代码在单独的线程中运行,但无法运行。 我尝试使用委托从 Internet 获取几个不同的多线程示例,但没有解决我的问题。

我需要通过 URL 从 XML 文件加载数据,然后在标签中显示来自 XML 的一些数据。加载 XML 有时需要很长时间,并且我的应用程序在加载过程中没有响应。我不知道我还应该尝试什么。

这是一个在没有多线程的情况下加载 XML 的示例(使 UI 无响应):

Dim xmlRoot1 As XElement = XDocument.Load("http://example.com/api/books.xml").Root
Label1.Text = xmlRoot1.<bookstore>.<book>(0).<title>.Value
Label2.Text = xmlRoot1.<bookstore>.<book>(1).<title>.Value
' ...

这是我正在加载的 XML 的示例:

<xml>
<bookstore>
    <book>
        <title>Everyday Italian</title>
        <author>Giada De Laurentiis</author>
        <year>2005</year>
        <price>30.00</price>
    </book>
    <book>
        <title>Harry Potter</title>
        <author>J K. Rowling</author>
        <year>2005</year>
        <price>29.99</price>
    </book>
    <book>
        <title>XQuery Kick Start</title>
        <author>James McGovern</author>
        <year>2003</year>
        <price>49.99</price>
    </book>
    <book>
        <title>Learning XML</title>
        <author>Erik T. Ray</author>
        <year>2003</year>
        <price>39.95</price>
    </book>
    </bookstore>
</xml>

如果您使用的是 Visual Studio 2012 和 4.5 框架或更高版本,您可以使用 AsyncAwait 关键字,这会让事情变得更容易。但是,您只能在 Task 对象上使用 Await 关键字。由于 XDocument 很遗憾没有提供 returns 一个 Task 对象的方便的 LoadAsync 方法,因此很难将其与 基于任务的异步模式一起使用(点击)。最简单的方法是创建一个这样的方法:

Public Async Function LoadXDocumentAsync(uri As String) As Task(Of XDocument)
    Dim t As New Task(Of XDocument)(Function() XDocument.Load(uri))
    t.Start()
    Return Await t
End Function

那么你可以这样称呼它:

Dim doc As XDocument = Await LoadXDocumentAsync("http://example.com/api/books.xml")
Label1.Text = doc.Root.<bookstore>.<book>(0).<title>.Value
Label2.Text = doc.Root.<bookstore>.<book>(1).<title>.Value

但是,通过调用 Task 对象上的 Start 方法,就像那样,它可能会启动一个新线程来完成工作。如果你担心解析 XML 需要很长时间,那是最好的办法,但如果你只关心下载时间,那么启动一个单独的线程只是为了在技术上效率低下在从 URI 下载 XML 时让它闲置。因此,虽然它有点复杂,但如果您只关心异步执行的下载,那么执行这样的操作在技术上更有效:

Public Async Function LoadXDocumentAsync(uri As String) As Task(Of XDocument)
    Dim client As New WebClient()
    Return XDocument.Parse(Await client.DownloadStringTaskAsync(uri))
End Function

然后您可以像第一个例子一样调用它。第二个示例利用了 WebClient class 确实提供了我们可以使用的基于任务的 Async 方法这一事实。因此,即使XDocument没有提供基于任务的Async方法,我们至少仍然可以使用基于任务的WebClient方法下载XML,然后,一旦我们得到它,只需在调用线程上用 XDocument 对象解析 XML 字符串。

据推测,微软将在框架的某些未来版本中向 XDocument class 添加一个 LoadAsync 方法,但在那之前你必须使 Async 自己动手,就像我在上面的例子中所做的那样。

如果您无法使用 TAP,我建议您使用 BackgroundWorker 组件。例如:

Public Class Form1
    Private _doc As XDocument

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        _doc = XDocument.Load("http://example.com/api/books.xml")
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        Label1.Text = _doc.Root.<bookstore>.<book>(0).<title>.Value
        Label2.Text = _doc.Root.<bookstore>.<book>(1).<title>.Value
    End Sub
End Class