2011-11-06 34 views
0

將網頁源代碼下載到備忘錄組件中的最快方式是什麼?我使用Indy和HttpCli組件。如何更快下載?

問題是,我有一個列表框,裏面裝滿了100多個網站,我的程序將源文件下載到備忘錄中,並解析該文件的源文件。它就像是一個Google音樂搜索程序。它使用Google查詢來簡化Google搜索。

我開始閱讀關於導致我的問題的線程:我可以在解析函數的線程中創建一個IdHttp實例,並告訴它解析列表框中的一半網站?

因此,基本上,當用戶點擊分析,主線程應該做的:

for i := 0 to listbox1.items.count div 2 do 
    get and parse 

,而其他線程應該做的:

for i := form1.listbox1.items.count div 2 to form1.listbox1.items.count - 1 do 
    get and parse. 

,所以他們將增加解析的內容form1.listbox2同一時間。或者,在主線程中啓動兩個IdHttp實例可能更容易;一半用於網站,另一半用於二次?

對於這個:我應該使用Indy還是Synapse?

+0

我建議您閱讀有關Synchronize的文檔,並讓每個線程在啓動時要求一個(且只有一個)URL,並且每次都處理一個URL。如果網站使用XHTML,我也會檢查MSXML2_TLB的DOMDocument.load方法,看看加載和解析是否執行得很好。 –

回答

9

我會創建一個線程,可以讀取一個URL並處理其內容。然後,您可以決定要同時觸發多少個線程。您的計算機將允許多個連接,因此如果這100個站點具有不同的主機名,則同時運行10個或20個並不是問題。太多太過於浪費,但太少會浪費處理器時間。

您可以通過單獨的線程進行下載和處理,進一步調整此過程,以便您可以讓大量線程不斷下載內容。下載並不是處理器密集型的。它基本上正在等待響應,因此您可以輕鬆地獲得相對較多的下載線程,而其他一些工作線程可以從結果池中獲取項目並處理它們。
但是拆分下載和處理會使它更復雜一些,我認爲你還沒有完全接受這個挑戰。

因爲目前您還有其他一些問題。起初,在一個線程中使用VCL組件是不行的。如果需要線程中的列表框中的信息,則需要在線程中使用Synchronize來對主線程進行「安全」調用,否則在啓動線程之前必須傳遞所需的信息。後者效率更高,因爲使用Synchronize執行的代碼實際上在主線程中運行,從而使您的多線程效率降低。

但我的注意實際上被吸引到第一行,「」下載網頁源代碼到備忘錄組件「。不要這樣做!不要將這些結果加載到備忘錄中進行處理。自動處理最好在視覺控制之外的內存中完成。使用字符串,流或甚至用於處理文本的字符串列表比使用備忘錄更快。
stringlist也有一些開銷,但是它使用相同的索引結構(TMemoStrings,它是Memo的Lines屬性和TStringList都具有相同的祖先),所以如果您獲得了使用此代碼的代碼,將其轉換爲TStringList將會非常容易。

+2

Downvoter,你能激勵你認爲是錯誤的嗎? – GolezTrol

+0

+1好方法,謝謝你指出使用備忘錄控制是一個壞主意。我發現了一個很不錯的線程安全的StringList,這將是得心應手這裏(用的Tilo埃克特TThreadStringList): http://www.swissdelphicenter.ch/torry/showcode.php?id=2167 它周圍的TStringList的包裝它使用關鍵部分來確保安全訪問底層字符串列表。 –

+0

將TThreadStringList用於下載項目列表確實很方便。每個下載的項目都可以推送到單獨的TThreadStringList進行處理。這樣,您可以像我建議的那樣分割下載和處理,而不會有太多麻煩。 – GolezTrol

5

我會建議做線程中的所有解析,沒有主線程做任何解析。主線程只應該管理UI。不要從TMemo解析HTML,將每個線程下載到TStream或String,然後直接解析。使用TIdSync或TIdNotify將分析結果發送到用戶界面進行顯示(如果速度很重要,請使用TIdNotify)。將UI組件包含在解析邏輯中會降低​​速度。

+1

如果解析器不僅僅是解析,而且正在做一些數據處理,它可能不是100%多線程安全的。恕我直言解析將比下載快得多。 –

+0

它只是下載和解析的東西,並stringreplace以取代%20,%3D等...在列表框中...... –

+0

所有這些都可以在不涉及UI的情況下完成,直到最終結果準備好顯示。將URL收集到TStringList中,根據列表條目的需要生成線程,其中每個線程下載到String或TMemoryStream(TIdHTTP支持兩者),解析數據並使用TIdNotify將結果發佈到主線程。這是線程安全的,避免了不必要的UI瓶頸。 –

4

Indy或Synapse都支持多線程。我建議使用比Indy輕得多的Synpase,並且足夠滿足您的需求。不要忘記微軟提供的HTTP APIs

簡單實現:

每URI
  • 一個線程;
  • 每個線程使用一個HTTP通信獲取數據;
  • 然後每個線程解析數據;
  • 然後使用Synchronize刷新UI。

也許我喜歡的:

  • 定義數目最大的線程中使用(例如8);
  • 這些線程中的每一個都將保持一個遠程連接(這是HTTP/1.1的目的,並且可以真正改變速度);
  • 所有請求都通過這些線程獲取一個接一個 - 不要預先指定URL線程,但是當一個線程完成一個從全局列表檢索新的URL(每個URL並不總是同一時間);
  • 螺紋可以等待,直到任何其他URI添加到全局列表(使用Sleep(100)或旗語例如);
  • 然後使用專用GDI消息(WM_USER+...)解析並更新主GUI線程中的用戶界面 - 解析將是快速的恕我直言(並記住用戶界面刷新速度很慢 - 例如看看BeginUpdate-EndUpdate方法) - 我發現GDI消息(與關聯的HTML數據)比使用阻止後臺線程的Synchronize更有效;
  • 另一種選擇是做解析在後臺線程,就已經獲取其URI數據後 - 也許不值得(僅當您的解析器慢),如果你的解析器可以進多線程問題/數據處理器不是100%線程安全的。

的第二點是如何流行所謂的「下載管理器」來實現。

當你處理多線程,你必須「保護」你的共享資源(列表,例如)。使用TCriticalSection來訪問任何全局列表(例如URI列表),並儘快釋放鎖。

,並嘗試與幾臺電腦和網絡,併發訪問,不同的操作系統來測試您的實現。調試多線程應用程序可能很困難,所以更簡單的實現更好:這就是爲什麼我建議讓下載部分爲多線程的原因,但讓主線程處理數據(這不會很大,所以它應該快)。

+0

你可以提供我簡單的代碼如何從列表中檢索網址,因爲我不知道如何從列表框中分裂100個網址到8個線程....我可以使變量鏈接並將它發送到thread.resume之前的線程,但如何在我開始之後給它鏈接謝謝 –

+1

@DanijelMaksimovicMaxa URI只是一個全局'TStringList',當它可以免費下載一個新文件時從每個線程中讀取。您*不*給這些線程分配URI,但是讓線程詢問關於任何剩餘URI的下載列表。您必須使用TCriticalSection保護對列表的訪問,以避免兩個線程一次檢索相同的URI。 –