3

すべてのソースコードを含むWebスパイダーアプリケーションを継承しました。通常のパンフレットスタイルのWebサイト(たとえば15ページ未満)の場合、ソフトウェアは完全に正常に動作しているようです。

その他の場合(20ページ以上)、ソフトウェアは以下のコードでマークされた行にStackOverflowExceptionをスローします。

再帰を利用しているようには見えません。残念ながら、使用されているLinqToHtml(SuperStarCoders)ライブラリはサポートされていません。

例外が発生したときに実行されるコードは次のとおりです。

   Private Function ExportXml(Optional ByVal _Worker As ComponentModel.BackgroundWorker = Nothing) As Boolean
    Dim _L = PopulateSEOList(_Worker)
    Try
        Dim _TmpStr As New Text.StringBuilder
        Dim _X As New XDocument, _ct As Long = 0, _Elements As Typing.SEO.Elements = Nothing
        ReportProgress(0, _Worker)
        With _TmpStr
            .Append("<?xml version=""1.0"" encoding=""UTF-8""?>")
            .Append("<o7th.Web.Design.Web.Spider>")
            For i As Long = 0 To _L.Count - 1
                _ct += 1
                .Append("   <Page>")
                .Append("       <Link>" & XmlEscape(_L(i).Link) & "</Link>")
                .Append("       <Title>" & XmlEscape(_L(i).Title) & "</Title>")
                .Append("       <Keywords>" & XmlEscape(_L(i).Keywords) & "</Keywords>")
                .Append("       <Description>" & XmlEscape(_L(i).Description) & "</Description>")
                .Append("       <Elements>")
                _Elements = _L(i).ContentElements
                If _Elements IsNot Nothing Then
                    If _Elements.H1 IsNot Nothing Then
                        .Append(<H1>
                                    <%= (From n In _Elements.H1.AsParallel()
                                        Select
                                        <Content><%= XmlEscape(n) %></Content>).ToList() %>
                                </H1>)
                    End If
                    If _Elements.H2 IsNot Nothing Then
                        .Append(<H2>
                                    <%= (From n In _Elements.H2.AsParallel()
                                        Select
                                        <Content><%= XmlEscape(n) %></Content>).ToList() %>
                                </H2>)
                    End If
                    If _Elements.H3 IsNot Nothing Then
                        .Append(<H3>
                                    <%= (From n In _Elements.H3.AsParallel()
                                        Select
                                        <Content><%= XmlEscape(n) %></Content>).ToList() %>
                                </H3>)
                    End If
                    If _Elements.H4 IsNot Nothing Then
                        .Append(<H4>
                                    <%= (From n In _Elements.H4.AsParallel()
                                        Select
                                        <Content><%= XmlEscape(n) %></Content>).ToList() %>
                                </H4>)
                    End If
                    If _Elements.H5 IsNot Nothing Then
                        .Append(<H5>
                                    <%= (From n In _Elements.H5.AsParallel()
                                        Select
                                        <Content><%= XmlEscape(n) %></Content>).ToList() %>
                                </H5>)
                    End If
                    If _Elements.H6 IsNot Nothing Then
                        .Append(<H6>
                                    <%= (From n In _Elements.H6.AsParallel()
                                        Select
                                        <Content><%= XmlEscape(n) %></Content>).ToList() %>
                                </H6>)
                    End If
                    If _Elements.UL IsNot Nothing Then
                        .Append(<UL>
                                    <%= (From n In _Elements.UL.AsParallel()
                                        Select
                                        <Content><%= ConvertToCDATA(n) %></Content>).ToList() %>
                                </UL>)
                    End If
                    If _Elements.OL IsNot Nothing Then
                        .Append(<OL>
                                    <%= (From n In _Elements.OL.AsParallel()
                                        Select
                                        <Content><%= ConvertToCDATA(n) %></Content>).ToList() %>
                                </OL>)
                    End If
                    If _Elements.STRONG IsNot Nothing Then
                        .Append(<STRONG>
                                    <%= (From n In _Elements.STRONG.AsParallel()
                                        Select
                                        <Content><%= XmlEscape(n) %></Content>).ToList() %>
                                </STRONG>)
                    End If
                    If _Elements.EM IsNot Nothing Then
                        .Append(<EM>
                                    <%= (From n In _Elements.EM.AsParallel()
                                        Select
                                        <Content><%= XmlEscape(n) %></Content>).ToList() %>
                                </EM>)
                    End If
                    If _Elements.BLOCKQUOTE IsNot Nothing Then
                        .Append(<BLOCKQUOTE>
                                    <%= (From n In _Elements.BLOCKQUOTE.AsParallel()
                                        Select
                                        <Content><%= ConvertToCDATA(n) %></Content>).ToList() %>
                                </BLOCKQUOTE>)
                    End If
                    If _Elements.A IsNot Nothing Then
                        .Append(<LINKS>
                                    <%= (From n In _Elements.A.AsParallel()
                                        Select
                                        <Content>
                                            <HREF><%= XmlEscape(n.Href) %></HREF>
                                            <REL><%= XmlEscape(n.Rel) %></REL>
                                            <TITLE><%= XmlEscape(n.Title) %></TITLE>
                                            <TARGET><%= XmlEscape(n.Target) %></TARGET>
                                            <CONTENT><%= XmlEscape(n.Content) %></CONTENT>
                                        </Content>).ToList() %>
                                </LINKS>)
                    End If
                    If _Elements.IMG IsNot Nothing Then
                        .Append(<IMAGES>
                                    <%= (From n In _Elements.IMG.AsParallel()
                                        Select
                                        <Content>
                                            <SRC><%= XmlEscape(n.Source) %></SRC>
                                            <ALT><%= XmlEscape(n.Alt) %></ALT>
                                            <TITLE><%= XmlEscape(n.Title) %></TITLE>
                                        </Content>).ToList() %>
                                </IMAGES>)
                    End If
                End If
                .Append("       </Elements>")
                .Append("       <Content><![CDATA[" & _L(i).Content.ToString() & "]]></Content>")
                .Append("   </Page>")
                ReportProgress((_ct / _L.Count) * 100, _Worker)
            Next
            .Append("</o7th.Web.Design.Web.Spider>")
        End With
        Dim _xStr As String = _TmpStr.ToString()
        _X = XDocument.Parse(_xStr)
        _X.Save(ExportPath & "site.xml")
        _X = Nothing
        ReportProgress(100, _Worker)
        Return True
    Catch ex As Exception
        'Put logging in here
        Message = ex.Message & ":::Export.ExportXml"
        Return False
    End Try
End Function

上記のLinkList変数は、(Typing.Linksの)リストです。

Partial Public Class Links
    Public Property SiteUrl As String
    Public Property SiteTitle As String
    Public Property Site As String
End Class

他の2つのリストは次のとおりです。

Imports Superstar.Html.Linq

Public Class Typing

Partial Public Class SEO

    Public Property Link As String
    Public Property Title As String
    Public Property Description As String
    Public Property Keywords As String
    Public Property Content As HElement
    Public Property ContentElements As Elements

    Partial Public Class Elements

        Public Property H1 As List(Of String)
        Public Property H2 As List(Of String)
        Public Property H3 As List(Of String)
        Public Property H4 As List(Of String)
        Public Property H5 As List(Of String)
        Public Property H6 As List(Of String)
        Public Property UL As List(Of String)
        Public Property OL As List(Of String)
        Public Property STRONG As List(Of String)
        Public Property BLOCKQUOTE As List(Of String)
        Public Property EM As List(Of String)
        Public Property A As List(Of Links)
        Public Property IMG As List(Of Images)

        Partial Public Class Images
            Public Property Source As String
            Public Property Alt As String
            Public Property Title As String
        End Class

        Partial Public Class Links
            Public Property Href As String
            Public Property Rel As String
            Public Property Title As String
            Public Property Target As String
            Public Property Content As String
        End Class

    End Class

End Class

End Class

ReportProgressは、この特定の状況でXamlウィンドウのバックグラウンドワーカーをレポートおよび更新して、進行状況バーを更新するだけです。

Public Sub ReportProgress(ByVal ct As Integer, _Worker As ComponentModel.BackgroundWorker)
    If _Worker IsNot Nothing Then
        _Worker.ReportProgress(ct)
        Threading.Thread.Sleep(500)
    End If
End Sub

、およびDownloaderクラスは次のとおりです。

Imports System.Reflection
Imports System.Net
Imports Superstar.Html.Linq

Public Class Downloader
Implements IDisposable

''' <summary>
''' Get the returned downloaded string
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property ReturnString As String
    Get
        Return _StrReturn
    End Get
End Property
Private Property _StrReturn As String

''' <summary>
''' Get the returned downloaded byte array
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property ReturnBytes As Byte()
    Get
        Return _FSReturn
    End Get
End Property
Private Property _FSReturn As Byte()

Private Property _UserAgent As String = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"
Private Property DataReceived As Boolean = False

''' <summary>
''' Download a string, but do not block the calling thread
''' </summary>
''' <param name="_Path"></param>
''' <remarks></remarks>
Public Sub DownloadString(ByVal _Path As String, Optional ByVal _Worker As ComponentModel.BackgroundWorker = Nothing)
    SetAllowUnsafeHeaderParsing20()
    Using wc As New Net.WebClient()
        With wc
            Dim _ct As Long = 0
            DataReceived = False
            .Headers.Add("user-agent", _UserAgent)
            .DownloadStringAsync(New System.Uri(_Path))
            AddHandler .DownloadStringCompleted, AddressOf StringDownloaded
            Do While Not DataReceived
                If _Worker IsNot Nothing Then
                    _ct += 1
                    ReportProgress(_ct, _Worker)
                End If
            Loop
        End With
    End Using
End Sub

''' <summary>
''' Download a file, but do not block the calling thread
''' </summary>
''' <param name="_Path"></param>
''' <remarks></remarks>
Public Sub DownloadFile(ByVal _Path As String, Optional ByVal _Worker As ComponentModel.BackgroundWorker = Nothing)
    SetAllowUnsafeHeaderParsing20()
    Using wc As New Net.WebClient()
        With wc
            Dim _ct As Long = 0
            DataReceived = False
            .Headers.Add("user-agent", _UserAgent)
            .DownloadDataAsync(New System.Uri(_Path))
            AddHandler .DownloadDataCompleted, AddressOf FileStreamDownload
            Do While Not DataReceived
                If _Worker IsNot Nothing Then
                    _ct += 1
                    ReportProgress(_ct, _Worker)
                End If
            Loop
        End With
    End Using
End Sub

''' <summary>
''' Download a parsable HDocument, for using HtmlToLinq
''' </summary>
''' <param name="_Path"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function DownloadHDoc(ByVal _Path As String, Optional ByVal _Worker As ComponentModel.BackgroundWorker = Nothing) As HDocument
    Try
        'StackOverFlowException Occurring Here!
        DownloadString(_Path, _Worker)
        Return HDocument.Parse(_StrReturn)
    Catch soex As StackOverflowException
        'put some logging in here, with the path attempted
        Return Nothing
    Catch ex As Exception
        SetAllowUnsafeHeaderParsing20()
        Return HDocument.Load(_Path)
    End Try
End Function

#Region "Internals"

Private Sub SetAllowUnsafeHeaderParsing20()
    Dim a As New System.Net.Configuration.SettingsSection
    Dim aNetAssembly As System.Reflection.Assembly = Assembly.GetAssembly(a.GetType)
    Dim aSettingsType As Type = aNetAssembly.GetType("System.Net.Configuration.SettingsSectionInternal")
    Dim args As Object() = Nothing
    Dim anInstance As Object = aSettingsType.InvokeMember("Section", BindingFlags.Static Or BindingFlags.GetProperty Or BindingFlags.NonPublic, Nothing, Nothing, args)
    Dim aUseUnsafeHeaderParsing As FieldInfo = aSettingsType.GetField("useUnsafeHeaderParsing", BindingFlags.NonPublic Or BindingFlags.Instance)
    aUseUnsafeHeaderParsing.SetValue(anInstance, True)
End Sub

Private Sub FileStreamDownload(ByVal sender As Object, ByVal e As DownloadDataCompletedEventArgs)
    If e.Cancelled = False AndAlso e.Error Is Nothing Then
        DataReceived = True
        _FSReturn = DirectCast(e.Result, Byte())
    Else
        _FSReturn = Nothing
    End If
End Sub

Private Sub StringDownloaded(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
    If e.Cancelled = False AndAlso e.Error Is Nothing Then
        DataReceived = True
        _StrReturn = DirectCast(e.Result, String)
    Else
        _StrReturn = String.Empty
    End If
End Sub

#End Region

#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls

' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
    If Not Me.disposedValue Then
        If disposing Then
        End If
        _StrReturn = Nothing
        _FSReturn = Nothing
    End If
    Me.disposedValue = True
End Sub

Public Sub Dispose() Implements IDisposable.Dispose
    Dispose(True)
    GC.SuppressFinalize(Me)
End Sub

#End Region

End Class

上で述べたように、再帰が発生しているようには見えません。(少なくとも私に本当に突き出ているものはありません)、それで私はすぐにそれが起こっているのはHDocument.Parse内にあると思います。

これがどこで間違っているのか、そして問題を修正する方法を教えてください。

私はいくつかの調査を行い、デフォルトのスタックサイズがわずか1MBであることを理解しているので、これは本当にこれを増やすことを試みる必要がある特別な状況の1つであるかどうか疑問に思います...

トレースを何度も見た後、特定のページにヒットしたときに常に発生することがわかりました。このページは、たまたまサイズが500kを超えています。

コールスタックは次のとおりです。

[External Code] 
>       o7th.Web.Design.Spider.Worker.dll!o7th.Web.Design.Spider.Worker.Downloader.DownloadHDoc(String _Path, System.ComponentModel.BackgroundWorker _Worker) Line 95 + 0x1e bytes  Basic
o7th.Web.Design.Spider.Worker.dll!o7th.Web.Design.Spider.Worker.Export.PopulateSEOList(System.ComponentModel.BackgroundWorker _Worker) Line 513 + 0x65 bytes    Basic
o7th.Web.Design.Spider.Worker.dll!o7th.Web.Design.Spider.Worker.Export.ExportXml(System.ComponentModel.BackgroundWorker _Worker) Line 70 + 0x1e bytes   Basic
o7th.Web.Design.Spider.Worker.dll!o7th.Web.Design.Spider.Worker.Export.RunExport(System.ComponentModel.BackgroundWorker _Worker) Line 30 + 0x17 bytes   Basic
o7th.Web.Design.WebSpider.exe!o7th.Web.Design.WebSpider.ParseLinks.RunExport(Object sender, System.ComponentModel.DoWorkEventArgs e) Line 106 + 0x2c bytes  Basic
[External Code] 

そしてLocalsは、私が上で述べた50万を超えるサイズのページを見せてくれます

4

2 に答える 2

2

(もっとスペースが必要でした。そうでなければ、@ Jakub Koneckiの投稿にコメントとして追加していました。)

私は何年にもわたっていくつかのスパイダーを構築してきましたが、並列処理のパフォーマンスが大幅に向上するのは、実際にURLをダウンロードすることだけです。大きなドキュメントで数百ミリ秒のHTML解析を削減することもできますが、その利益はデバッグ価格の価値がありません。だからあなたの人生を楽にして、並列性を取り除いてください。

また、奇妙な非同期ブロッキングの問題があります。同期的にDownloadHDoc呼び出しているDownloadStringが、内部でDownloadStringは非同期メソッドを開始し、ビットフラグをブロックして、非同期の目的を無効にしている。さらに悪いことに、do-while時速100万マイルで回転し、ReportProgress毎回電話をかけているループでブロックしているということです。これが実際にあなたにSOEを与えているものだと思います。そこThread.Sleep(100)に入れると、初心者に役立つかもしれません。

[編集]

ビットフラグをブロックしているコードは次のとおりです。

        .DownloadStringAsync(New System.Uri(_Path))
        AddHandler .DownloadStringCompleted, AddressOf StringDownloaded
        Do While Not DataReceived
            If _Worker IsNot Nothing Then
                _ct += 1
                ReportProgress(_ct, _Worker)
            End If
        Loop

1行目は非同期メソッドを開始し、2行目は完了のハンドラーを追加してすぐに戻ります。3行目は、グローバル変数を何度もチェックしており、関数StringDownloadedが設定するのを待っています。これは毎秒数百または数千(またはそれ以上)の回数発生しています。最適ではありませんが、悪いのはReportProgress毎回メソッドを呼び出していることです。ドキュメントが大きいほど、より多くの呼び出しReportProgressが行われます。本当に必要なのはせいぜい100msごとにUIを更新するだけです。私は通常、250msまたは500msごとに設定します。

[編集2]

上記が問題だった場合は、次のように変更できるはずです。

    .DownloadStringAsync(New System.Uri(_Path))
    AddHandler .DownloadStringCompleted, AddressOf StringDownloaded
    Do While Not DataReceived
        If _Worker IsNot Nothing Then
            _ct += 1
            ReportProgress(_ct, _Worker)
        End If
        Thread.Sleep(250) ''//Sleep inside of the loop
    Loop
于 2012-12-28T15:20:41.350 に答える
0

私はすべての並列処理を削除することから始めます-とにかくそれはおそらく過剰であり、複数のスレッドを作成するオーバーヘッドはパフォーマンスの向上よりも大きくなります。

これを行ったら、コードをデバッグして例外を待ちます。その後、コールスタックとすべてのコレクションを確認できます。

スタックオーバーフローは通常、同じメソッドを再帰的に呼び出し、何らかの理由で終了条件が開始されない場合に発生します。コールスタックにはっきりと表示されます。

于 2012-12-28T14:53:46.103 に答える