Delphi 的平行程式函式庫提供 TTask.Future 簡化非同步操作,適用於耗時任務如複雜計算或 I/O 操作。建立 Future 只需呼叫 TTask.Future 並傳入對應函式。透過 Value 屬性取得結果,但需注意阻塞問題。使用匿名方法則可在運算後於主執行緒執行回呼。TParallel.For 將迴圈平行化,提升效率,但需注意資料分享問題,使用 TInterlocked 系列函式可避免此問題。將 TParallel.For 包裝在 Future 或 Async/Await 中可避免阻塞。管線模式則將任務分解成多階段平行處理,各階段間透過佇列傳遞資料,提升效率。網路蜘蛛的實作可有效展現管線模式的優勢,其包含過濾、下載和解析三個階段,各階段負責不同任務,最後將結果整合。
深入探索平行程式設計實踐
使用 TTask.Future 進行非同步運算
在 Delphi 的平行程式函式庫(Parallel Programming Library, PPL)中,TTask.Future 提供了一種簡便的方式來執行非同步運算並在稍後取得結果。這種機制特別適合於那些需要較長時間執行的任務,例如複雜的計算或 I/O 操作。
建立 Future
建立一個 Future 非常簡單,只需呼叫 TTask.Future<integer> 並傳入一個傳回整數的函式或匿名方法即可。例如,下面的程式碼展示瞭如何使用 TTask.Future 來執行一個名為 CountPrimes 的函式,該函式計算某個範圍內的質數數量:
procedure TfrmFuture.btnFutureClick(Sender: TObject);
begin
FFuture := TTask.Future<integer>(CountPrimes);
end;
取得 Future 的結果
一旦 Future 被建立,你可以透過存取其 Value 屬性來取得運算結果。如果運算尚未完成,存取 Value 將會阻塞直到結果可用。下面的程式碼展示瞭如何取得 Future 的結果並將其顯示在列表框中:
procedure TfrmFuture.btnGetValueClick(Sender: TObject);
begin
ListBox1.Items.Add('Result = ' + FFuture.Value.ToString);
FFuture := nil;
end;
使用匿名方法改進 Future
另一種使用 Future 的方式是傳入一個匿名方法,這樣可以在運算完成後於主執行緒中執行特定的回呼方法。以下是一個範例:
procedure TfrmFuture.btnFuture2Click(Sender: TObject);
begin
FFuture := TTask.Future<integer>(
function: Integer
begin
Result := CountPrimes;
TThread.Queue(nil, ReportFuture);
end);
end;
procedure TfrmFuture.ReportFuture;
begin
ListBox1.Items.Add('Result = ' + FFuture.Value.ToString);
FFuture := nil;
end;
處理 Future 中的例外
如果在 Future 的運算過程中丟擲例外,該例外將被 PPL 捕捉並在主執行緒存取 Value 屬性時重新丟擲。因此,建議在背景任務中自行處理例外。
平行 For 迴圈
PPL 提供了一個多執行緒版本的 for 迴圈,即 TParallel.For。它允許簡單地將迴圈平行化,但使用時須小心避免資料分享問題。
基本用法
要將一個標準的 for 迴圈轉換為平行 for,只需呼叫 TParallel.For 並傳入迴圈範圍和工作方法即可。例如:
procedure TbtnParallelFor.btnParalleForBadClick(Sender: TObject);
var
count: Integer;
sw: TStopwatch;
begin
sw := TStopwatch.StartNew;
count := 0;
TParallel.For(2, CHighestNumber,
procedure (i: integer)
begin
if IsPrime(i) then
Inc(count); // 這裡存在資料分享問題
end);
sw.Stop;
// ...
end;
修復資料分享問題
上述範例中,多個執行緒同時更新 count 變數,導致資料競爭。為瞭解決這個問題,可以使用 TInterlocked.Increment 取代 Inc:
TParallel.For(2, CHighestNumber,
procedure (i: integer)
begin
if IsPrime(i) then
TInterlocked.Increment(count);
end);
使用 Future 包裝 Parallel For
由於 TParallel.For 會阻塞呼叫執行緒直到運算完成,因此可以將其包裝在 Future 或 Async/Await 中以實作非阻塞執行。例如:
procedure TbtnParallelFor.btnAsyncParallelForClick(Sender: TObject);
begin
// 使用 Future 包裝 ParallelCountPrime 函式
end;
平行處理實務探討
在現代軟體開發中,平行處理技術已成為提升應用程式效能的重要手段。Delphi 的平行程式函式庫(Parallel Programming Library)提供了一系列強大的工具,使開發者能夠輕鬆實作平行處理。本文將探討平行處理中的幾個重要概念,包括 TParallel.For、Future 模式以及管道(Pipeline)模式。
使用 TParallel.For 進行平行迴圈
TParallel.For 是 Delphi 平行程式函式庫中的一個重要功能,它允許開發者將傳統的迴圈轉換為平行執行的任務。以下是一個簡單的範例,展示如何使用 TParallel.For 來計算指定範圍內的質數數量:
function TbtnParallelFor.ParallelCountPrimes: Integer;
var
count: Integer;
begin
count := 0;
TParallel.For(2, CHighestNumber,
procedure(i: Integer)
begin
if IsPrime(i) then
TInterlocked.Increment(count);
end);
Result := count;
TThread.Queue(nil,
procedure
begin
FStopwatch.Stop;
ListBox1.Items.Add('非同步平行迴圈: ' + FParallelFuture.Value.ToString + ' 個質數。總耗時: ' + FStopwatch.ElapsedMilliseconds.ToString);
FParallelFuture := nil;
end);
end;
內容解密:
TParallel.For:用於將迴圈平行化,自動分配多個執行緒來處理迴圈中的任務。TInterlocked.Increment:用於執行緒安全地增加計數器count的值,避免多執行緒同時存取造成的資料競爭。TThread.Queue:將匿名函式加入主執行緒的佇列中執行,確保 UI 更新在主執行緒中進行。
Future 模式與非同步處理
Future 模式允許開發者在背景執行緒中執行任務,並在任務完成後取得結果。以下範例展示瞭如何使用 TTask.Future 來非同步執行 ParallelCountPrimes 函式:
begin
FStopWatch := TStopwatch.StartNew;
FParallelFuture := TTask.Future<Integer>(ParallelCountPrimes);
end;
內容解密:
TTask.Future:建立一個非同步任務,並傳回一個IFuture<T>介面,用於取得任務的結果。ParallelCountPrimes:在背景執行緒中執行的函式,負責計算質數數量。
例外處理
在使用 TParallel.For 時,若迴圈中的任務丟擲例外,這些例外將被捕捉並重新拋出在主執行緒中。開發者可以使用 try..except 區塊來處理這些例外。以下範例展示瞭如何捕捉並記錄 TParallel.For 中的例外:
procedure TbtnParallelFor.btnParallelForExceptionClick(Sender: TObject);
var
i: Integer;
begin
ListBox1.Items.Add('
---
');
try
TParallel.For(1, 10,
procedure(i: Integer)
begin
Sleep(100);
raise Exception.Create('執行緒 ' + TThread.Current.ThreadID.ToString + ' 中的例外');
end);
except
on E: EAggregateException do
for i := 0 to E.Count - 1 do
if not Assigned(E[i]) then
ListBox1.Items.Add(i.ToString + ': nil')
else
ListBox1.Items.Add(i.ToString + ': ' + E[i].ClassName + ': ' + E[i].Message);
on E: Exception do
ListBox1.Items.Add(E.ClassName + ': ' + E.Message);
end;
end;
內容解密:
EAggregateException:當TParallel.For中的多個任務丟擲例外時,這些例外將被收集在EAggregateException中。- 例外處理:透過迴圈遍歷
EAggregateException中的例外,並記錄每個例外的詳細資訊。
管道(Pipeline)模式
管道模式是一種將任務分解為多個階段,並在不同執行緒中平行執行的技術。每個階段處理資料的一部分,並將結果傳遞給下一個階段。以下是一個簡單的管道模式範例,用於實作一個簡單的網頁爬蟲:
// 簡化的管道模式範例,用於網頁爬蟲
procedure TPipelineDemo.Start;
begin
// 初始化管道階段
Stage1 := TStage.Create(ProcessURL);
Stage2 := TStage.Create(ProcessHTML);
// 連線階段
Stage1.NextStage := Stage2;
// 啟動管道
Stage1.Start;
end;
procedure TPipelineDemo.ProcessURL(const Input: string);
begin
// 處理 URL
var html := DownloadHTML(Input);
Stage1.Send(html);
end;
procedure TPipelineDemo.ProcessHTML(const Input: string);
begin
// 處理 HTML 內容
var links := ExtractLinks(Input);
// 將結果傳遞給下一個階段或輸出
end;
圖表翻譯:
此圖示展示了一個簡單的管道流程,其中資料從一個階段傳遞到下一個階段,每個階段在不同的執行緒中平行執行。
管道模式流程圖
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 圖表翻譯:
rectangle "HTML" as node1
rectangle "連結" as node2
node1 --> node2
@enduml
網路蜘蛛管線實作探討
網路蜘蛛(Web Spider)是一種自動化程式,能夠遍歷網頁並提取所需資訊。本文將探討如何使用管線(Pipeline)架構來實作網路蜘蛛,並介紹其內部實作機制。
網路蜘蛛管線介面
網路蜘蛛管線的介面非常簡單,主要包含啟動、停止及事件處理等功能。以下為簡化的介面定義:
type
TWebSpider = class
public
procedure Start(const baseUrl: string);
procedure Stop;
property OnPageProcessed: TProc<string> read FOnPageProcessed write FOnPageProcessed;
property OnFinished: TProc read FOnFinished write FOnFinished;
end;
使用者可以透過 Start 方法啟動網路蜘蛛,並傳入初始 URL。OnPageProcessed 事件會在每個網頁處理完成後觸發,而 OnFinished 事件則在整個處理過程完成後觸發。
網路蜘蛛管線實作
網路蜘蛛管線內部包含三個主要階段:
- 過濾階段(Filter Stage):接收 URL 並檢查是否已經處理過,若未處理則傳遞給下載階段。
- 下載階段(Downloader Stage):接收 URL 並下載其內容,若成功則將 URL 和 HTML 傳遞給解析階段。
- 解析階段(Parser Stage):解析 HTML 並提取相關 URL,將其傳回過濾階段進行處理。
管線設定
管線的設定是在 Start 方法中完成的,程式碼如下:
procedure TWebSpider.Start(const baseUrl: string);
var
i: integer;
begin
FPipeline := TPipeline<string, string>.Create(
10000, 100,
procedure
var
url: string;
begin
if assigned(OnPageProcessed) then
while FPipeline.Output.Read(url) do
OnPageProcessed(url);
end);
// 建立管道
FHttpGetInput := FPipeline.MakePipe<string>(100);
FHtmlParseInput := FPipeline.MakePipe<THttpPage>(10);
// 設定階段
FPipeline.Stage('Unique filter',
procedure
begin
Asy_UniqueFilter(baseUrl, FPipeline.Input, FHttpGetInput);
end);
for i := 1 to TThread.ProcessorCount do
FPipeline.Stage<string, THttpPage>('Http get #' + i.ToString, FHttpGetInput, FHtmlParseInput, Asy_HttpGet);
FPipeline.Stage<THttpPage, string>('Html parser', FHtmlParseInput, FPipeline.Input, Asy_HtmlParse);
FPageCount := 1;
FPipeline.Input.Write(baseUrl);
end;
管線運作機制
管線的運作機制如下:
- 當使用者呼叫
Start方法時,管線會被建立並啟動。 - 過濾階段接收初始 URL 並檢查是否已經處理過,若未處理則傳遞給下載階段。
- 下載階段接收 URL 並下載其內容,若成功則將 URL 和 HTML 傳遞給解析階段。
- 解析階段解析 HTML 並提取相關 URL,將其傳回過濾階段進行處理。
- 當網頁處理完成後,
OnPageProcessed事件會被觸發。 - 當整個處理過程完成後,
OnFinished事件會被觸發。
管線優點與挑戰
使用管線架構來實作網路蜘蛛具有以下優點:
- 模組化:管線架構將整個處理過程分解為多個獨立的階段,使得程式碼更容易維護和擴充。
- 平行處理:管線架構可以輕易地實作平行處理,提高程式的執行效率。
然而,管線架構也面臨一些挑戰:
- 死鎖問題:當管線中的某個階段被阻塞時,可能會導致整個管線死鎖。
- 資源管理:管線架構需要妥善管理資源,避免資源浪費或不足。
圖表說明
此圖示展示了網路蜘蛛管線的內部結構:
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 圖表說明
rectangle "URL" as node1
rectangle "HTML" as node2
node1 --> node2
@enduml
圖表翻譯: 此圖表呈現了網路蜘蛛管線的三個主要階段及其之間的資料流動。過濾階段接收 URL 並將其傳遞給下載階段,下載階段下載 HTML 內容並將其傳遞給解析階段,解析階段提取相關 URL 並將其傳回過濾階段。
程式碼解析
FPipeline.Stage('Unique filter',
procedure
begin
Asy_UniqueFilter(baseUrl, FPipeline.Input, FHttpGetInput);
end);
內容解密:
此段程式碼設定了管線的第一個階段:過濾階段。Asy_UniqueFilter 程式負責檢查 URL 是否已經被處理過,如果沒有,則將其傳遞給下載階段。這個階段確保了相同的 URL 不會被重複處理。
for i := 1 to TThread.ProcessorCount do
FPipeline.Stage<string, THttpPage>('Http get #' + i.ToString, FHttpGetInput, FHtmlParseInput, Asy_HttpGet);
內容解密:
這段程式碼根據系統的處理器數量建立多個下載階段。每個下載階段都會呼叫 Asy_HttpGet 程式來下載指定的 URL 對應的網頁內容。這樣可以充分利用多核心處理器的能力,提高下載效率。
FPipeline.Stage<THttpPage, string>('Html parser', FHtmlParseInput, FPipeline.Output, Asy_HtmlParse);
內容解密:
這段程式碼設定了解析階段。Asy_HtmlParse 程式負責解析下載下來的 HTML 內容,提取出其中的 URL,並將結果輸出到管線的輸出端。這個階段是將下載的內容轉換為有用的資料的關鍵步驟。
網路蜘蛛的應用與未來發展
網路蜘蛛技術廣泛應用於搜尋引擎、資料採集、網路監控等領域。隨著網路技術的不斷發展,網路蜘蛛技術也在不斷進步,例如採用更先進的網頁解析技術、提升反爬蟲策略等。未來,網路蜘蛛技術可能會與人工智慧、大資料等技術結合,實作更智慧、更高效的資料採集和分析。