演示在Delphi中使用線程的不同方法

太多的Delphi用戶犯了一個錯誤,即認為線程是某種魔術,可以改善其應用程序的性能。不幸的是,這遠非如此。試圖實現線程時#1的最大錯誤是使其直接訪問應用程序的視覺控制。但是這些視覺控制只能在應用程序主線程的上下文中起作用。必須使用另一個線程來更新用戶界面中的控件,必須非常仔細地計劃和實現。在大多數情況下,這可能根本不是解決問題的正確解決方案。
簡而言之,Delphi的VCL框架不是安全的。儘管有許多方法可以將線程集成到您的UI中,但沒有單一的單一適合解決方案。它總是會根據您要完成的目標而有所不同。在大多數情況下,人們希望取得更好的性能(速度)。但這很少是通過使用線程來完成的。取而代之的是,將線程集成到用戶界面中的最常見方案是在長期任務中保持UI響應。
為此,我們會想像一個只有一個按鈕的簡單應用程序,該應用程序在單擊時從Internet下載文件。該應用程序已經具有一個用於整個UI的主線程。在Windows平台上,這意味著發送/接收Windows消息,繪製到控制畫布,識別用戶交互等。此線程本質上是一個巨大的環路,非常快速地旋轉。對於此旋轉線的每一次革命,執行了某些代碼。
在單個螺紋環境中,此文件下載將阻止此循環旋轉,直到下載完成。在此期間,此線程不再能夠執行任何UI更新,檢測用戶點擊或其他任何內容。這就是導致Windows在此類形式的標題中放置(不響應)的原因,因為就像它所說的那樣,它沒有響應。
這是其他線程進來的地方。它需要響應窗口。您可以將該文件下載放入另一個線程中,而不是通過此巨型文件下載來阻止主UI線程。這很簡單,對嗎?
您可能會問自己“我該如何監視進度?”或“完成後如何通知?”這意味著下載線程需要以某種方式與主線程進行交互。這正是混亂的出現。一個線程不能簡單地干擾另一個線程,因為沒有說明一個線程實際上是什麼。現在有兩個單獨的循環,當您想更新UI時,UI線程可以在任何地方做任何事情。最重要的是,假設主UI線程是在將字符串編寫到同一控制屬性的過程中,您的另一個線程也想寫入。現在,您有兩個線程試圖寫入相同的內存地址,這可能會導致不可預測的問題。
通過同步。 Delphi的TThread類具有方法同步(),該類允許線程僅在實際期望發生這種情況的時候才能與Main UI線程進行交互。從另一個線程同步的代碼實際上並未在該線程的上下文中運行 - 它總是在主UI線程的上下文中運行。這就是同步()的想法,就是在UI線程中執行代碼。
因此,最後,您實際上並未使用線程中的VCL。取而代之的是,您的線程將信號發送到主線程,並且只有在準備好主線程時,它才能執行該代碼。同時,您的輔助線程在等待主線程完成時被阻止。
然後是一個錯誤,認為大型UI操作在線程中會更好。假設您有一個列表,您想在這裡填充數百萬個物品。當然,這將需要時間,在此期間,您的申請將無法響應。再次。因此,只需將代碼移至線程,對嗎?
同樣,必須從主線程和主線程進行任何UI交互。如果您需要執行冗長的計算,處理大量數據,等待遠程資源的響應或其他任何既耗時又與UI直接相關的內容,則線程很有用。
這很難說。但是,在編寫線程時,有一個共同的做法:將您的線程代碼放在自己的單元中。該單元應與任何其他UI單元隔離。它甚至不應在其用途子句中具有任何相關單元。該線程甚至不知道如何使用它。它本質上應該是一個假人,其唯一目的是執行冗長的任務。當涉及到線程UI更新時,最好通過同步事件來完成。
正是聽起來的樣子。如前所述,這是一個同步的事件。事件只是指向過程的指針,您可以在線程啟動之前分配給該過程。在線程內部,當您需要更新UI時,您將使用Synchronize()觸發此事件。通過這種設計,該線程永遠不必知道它甚至被UI使用。同時,您也無意中完成了抽象。線程變得可重複使用。您可以將其插入其他可能甚至沒有用戶界面的項目(例如Windows服務)。
這是一些有關VCL線程安全的相關資源的直接鏈接,以防您不想搜索...
該應用程序演示了Delphi中線程的用法。由於有許多不同的知識,因此出於不同的目的,它們分為不同的部分。每個主題至少具有1個表單單元(嵌入到選項卡表中),除用戶界面外,至少有1個獨立單元包含其功能。這是故意完成的,以表明應與任何UI隔離線程。
主要形式本身實際上沒有任何邏輯。它所做的只是將表單嵌入到選項卡中。在FormCreate()事件處理程序中,它對EmbedForm()進行了許多調用,該調用為每個標籤表實例化。
實際使用該應用程序非常簡單。您只需導航到其中一個標籤,每個標籤都會有自己的說明。


顯示如何在線程中從Internet下載文件。有一個單一的通用函數定義的DownloadFile()執行下載。 UI有3個按鈕:
默認情況下,要下載的URL是由ThinkBroadband.com提供的測試文件,但是您可以使用任何想要的URL。您也可以選擇將文件保存到的位置。這是一個非常簡單的演示,因此需要根據您的需求調整本地文件名的文件名/擴展名 - 它不會自動為您下載的URL更改(如瀏覽器通常這樣做)。

顯示如何從執行冗長任務的線程中更新進度條。

在線程中使用數據庫連接並將數據同步到UI線程中。

演示多個螺紋消耗大量CPU週期以加載測試您的處理器。