熱度 1||
VB/COM 多線程概述 |
|
這篇指南提供了一篇關于使用Visual Basic和Thread Factory開發(fā)多線程COM應用程序的概述。它假設讀者對COM和COM線程模型(包括COM STA )已經有一定的了解。其中不會包含MTA(Multi Threaded-Apartment,多線程套間),主要是因為在Visual Basic不能創(chuàng)建MTA對象。
一個多線程程序是一個可以在同一個應用程序當中同時執(zhí)行2個或者更多的任務的程序 ,多任務是通過分別指派一個單獨的線程來執(zhí)行一個任務來實現的。一個線程基本上就是一個程序代碼執(zhí)行的一個路徑,同時它也是在Win32調度器當中可以識別的最小的執(zhí)行單元。一個進程由一個或者多個線程以及程序的代碼、數據和程序中其他在內存 中的資源組成。Windows的調度器決定哪個線程應該運行以及什么時候運行。在多處理器計算機上,調度器可以通過切換單個線程到不同的處理器上來平衡CPU的負載。
在使用得當的情況下,多線程技術可以明顯提高一個應用程序的性能和反應速度,但如果使用不當,它也可以令一個程序變慢。經驗是設計多線程程序的最好的老師,沒有硬性規(guī)定什么時候應該使用多線程技術。可以通過仔細檢查程序 ,來看看到底將哪個任務單獨放到另外的線程中運行會更有用,又或者檢查看看哪些任務如果同時運行會使程序更好,以上兩點都是比較好的切入點。
Visual Basic是一個可以用來創(chuàng)建Windows應用程序和COM ActiveX組件的開發(fā)工具。Visual Basic可以創(chuàng)建一個在COM STA 中安全運行的ActiveX DLL組件。Visual Basic在創(chuàng)建組件提供兩種線程模型。它們是“單線程模型(Single Thread)”(所有對象都運行在一個STA中)以及“套間線程 模型(Apartment Threaded)”(每個對象都可以在一個各自不同的STA中運行)。盡管Visual Basic允許你創(chuàng)建基于套間線程模型的可以運行在另外套間的ActiveX DLL對象,但它總是使用同一個線程在主STA中創(chuàng)建這些對象。因此使用ActiveX DLL對象的Visual Basic應用程序是單線程的。
Thread Factory™提供一個 類庫,用于專門通過在另外的STA套間中中創(chuàng)建COM對象(使用另外的線程) 的方式來克服這個限制。在不同于應用程序主線程的線程中創(chuàng)建的對象,我們稱之為工作者對象(Worker Objects)(即上邊運行著工作者對象的工作者線程(Worker Thread))。
Thread Factory 中有一個AsyncObject對象(Asynchronous Object,異步對象)是專門用來創(chuàng)建工作者對象的。一個AsyncObject對象是 一個輕型包裝類,它用于在一個單獨的STA套間中創(chuàng)建工作者對象,而且這個STA套間運行在工作者線程上。除了創(chuàng)建工作者對象以外,AsyncObject對象也會接管所有和這個工作者對象有關的來 往傳輸的COM調用、異步調用和異步調錯誤處理。每個工作者對象都被一個單獨的AsyncObject對象包裹住。
備注: Thread Factory只能夠為那些被標記為支持Apartment線程模型或者兩者線程模型都同時支持的對象創(chuàng)建工作者對象。
普通的Visual Basic對象創(chuàng)建過程(單線程)
| Dim oPerson as clsPerson Set oPerson = new clsPerson '對象在 主STA 及線程中創(chuàng)建 |
Thread Factory的對象創(chuàng)建過程(多線程)
| Dim oAsyncObject as new ThreadFactoryLib.AsyncObject Dim oPerson as clsPerson Set oPerson = oAsyncObject.Create("PersonLib.clsPerson") '對象在另外的STA及線程中創(chuàng)建 |
當用一個AsyncObject對象創(chuàng)建一個新的 對象(工作者對象)時, 開始它首先會在一個另外的線程上創(chuàng)建一個新的STA套間,然后會在新的STA套間中創(chuàng)建工作者對象的一個實例。
備注: 如果在一個工作者對象的代碼中使用New關鍵字來創(chuàng)建一個其他的新的工作者對象,那這個工作者對象會和新創(chuàng)建的工作者對象共享同一個套間和線程。
如果關閉應用程序的主線程(主STA),則會引起所有的工作者都同時關閉。
要想產生一個真正的多線程程序,通過在一個另外的線程中運行工作者對象這樣的方式這是不夠的。因為所有的Visual Basic COM調用都是同步的,這使得兩個線程不可能同時運行(除非你使用定時器來欺騙并且繞過COM)。AsyncObject對象公開了一個稱之為IBlindDelegator的特殊接口 ,這個接口使得要實現 異步調用變更得更容易。這個IBlindDelegator接口會通過Begin_和Finish_方法來平行化COM+異步調用。
我們應用異步調用時,需要使用特殊的錯誤處理方式。錯誤一般是通過調用堆棧來返回的,而由異步調用引起的錯誤會被丟棄,這是因為那時的調用堆棧是空的,而那時 我們沒有一個可以將這個錯誤返回的上層函數。由于所有由Thread Factory創(chuàng)建的工作者對象都在AsyncObject對象中包裝著,AsyncObject對象會攔截所有在異步調用時所產生的錯誤并且將他們轉換為AsyncObject對象的OnError事件,這樣他們就不會被丟棄了。
在應用程序的開發(fā)過程中,最重要的步驟之一就是調試。Thread Factory通過允許你安全的使用Visual Basic的調試器,從而使調試變得簡單。由于Visual Basic的調試器是為單線程應用程序的調試而設計的,在調試不同的多線程應用程序的項目時,都會有一些不同的限制。
當調試一個不包含ActiveX DLL源代碼的工程時:
工作者對象會在一個另外的線程它自身的STA中創(chuàng)建,但這時這個被調試的工程當中產生的所有對于這個工作者對象的調用都會被串行化(即使使用IBlindDelegator接口也是這樣)。
當調試一個包括ActiveX DLL源代碼的工程時:
Visual Basic會強制所有的工作者對象都在同一個主STA線程中創(chuàng)建。調試這一類項目是和調試一個普通的單線程應用程序是一模一樣的。
提示: 微軟的Visual C++調試器是一個非常好的用來代替Visual Basic的調試器的代替品。不像VB的調試器,Visual C++的調試器設計時就有考慮到多線程程序的調試。要使用Visual C++來調試VB的應用程序和組件,你首先必須要編譯項目時勾選“產生符號化調試信息”選項。
備注: 當你運行完一個多線程的工程后關閉Visual Basic的IDE時,可能會引發(fā)一個異常(見下圖)。這是因為可能在調試時我們曾經以按下“停止”按鈕的方式來中斷調試過程,又或者在程序代碼中包含了END語句 ,并且試圖以它來中斷程序的運行。這會導致Visual Basic沒有正常讓清理代碼執(zhí)行,從而出現了下面的異常。這個異常只會在你關閉IDE時出現,絕對不會在運行編譯后的程序時出現。一個盡量減少這種偶然錯誤的 方法是,在另外的DLL的工作者對象它們的創(chuàng)建和銷毀兩個事件過程中加入相應的代碼來處理這些工作。這樣之所以能解決問題,是因為類的Terminate事件它不管我們在什么樣的情況下退出程序 ,它都會被執(zhí)行。Progress示例程序就體現了這點(譯者注:這一段我沒有按照完全原文逐字翻譯,而按照我的理解來譯)。
當要和一個另外的線程或者進程通信時是會用到封裝傳送的。封裝傳送是在進行超出線程或進程邊界的方法調用時,完成打包和發(fā)送的行為 的體現。COM使用 Proxy 和 Stub 來封裝傳送那些在線程/進程間的調用。
在由Thread Factory所創(chuàng)建的工作者對象之間通信速度是非?斓,這是因為這些工作者對象都是在同一個進程里的不同線程間運行的(In-Process,進程內)。 而ActiveX EXE 服務器模型雖然也可以創(chuàng)建工作者對象,但是 在它創(chuàng)建的進程之間通信非常慢,這是因為在調用外部進程時會產生額外的針對調用的封裝傳送工作(Out-Of-Process,進程外)。
下邊的插圖展示了在Thread Factory和ActiveX EXE服務器模型兩種模型中,產生與其他工作者對象通信的情況時涉及的封裝傳送流程。
Thread Factory | Active X EXE 服務器模型 |
盡管Thread Factory和ActiveX EXE服務器模型兩種模型都是創(chuàng)建工作者對象的有效方法,但是Thread Factory提供很多ActiveX EXE服務器模型所沒有的優(yōu)勢,包含:
性能:使用Thread Factory的通信速度快很多
內置異步調用架構(包含取消調用的操作)
內置異步錯誤處理
異步調用超時及強制結束處理
支持單獨檢查每個工作者對象的狀態(tài)
很容易在一個單獨的Visual Basic會話中調試
支持處理特定的資源,比如UDTs(譯者注:用戶自定義結構體)、hDC句柄、臨界區(qū)域和hEvents句柄
支持創(chuàng)建多線程OCX組件
同步問題在開多線程應用程序時非常重要 ,這是因為它可以保證線程的安全。我們通常使用同步對象來防止多個線程在同時訪問同一個變量或者系統(tǒng)資源時發(fā)生沖突問題。Thread Factory所創(chuàng)建的工作者對象,由COM所提供的一個特性來解決同步問題,這個特性主要指COM強制所有的工作者對象都必須在自身的STA中創(chuàng)建,并且COM串行化所有 外部對它們的訪問(譯者注:這一句是按照我自身的理解調整翻譯的,沒有完全按照原文一字一詞的譯)。串行化調用天生就很安全,這是因為在給定的任意時刻只有內 ,所有的對于對象的調用當中,只能有單個方法被執(zhí)行。如果兩個工作者對象試圖同時調用同一個另外的工作者對象的話,則兩者的調用都會被串行化,由此,只有其中一個工作者對象會得到他們想要的 ,而另一個工作者對象的操作則必須等到第一個調用操作執(zhí)行完成后才會被執(zhí)行。
很多時候在非線程安全的情況下,你必須手動添加代碼以防止發(fā)生同時對一個資源或者對象的訪問沖突問題。在一個多線程程序中資源或者對象不是線程安全的原因有很多,但通常通過串行化訪問可以使得它 們變成線程安全的。這可以通過使用Windows提供的眾多同步對象當中的某一種來實現。為了方便起見,Thread Factory在它的類庫中提供了一些關于臨界區(qū)域(Critical Sections)和互斥量(Mutexes) 的API調用聲明(譯者注:你可以直接在VB中使用這些技術而且不需要額外的聲明它們)——詳見Win32 API調用。
在開發(fā)多線程應用程序時全局變量是另外一個問題,這是因為全局變量是暴露的,這使得所有的對象(包括工作者對象)都可以同時訪問它。Thread Factory的工作者對象使用TLS(Thread Local Storage,線程本地存儲)技術,來每為個線程都保存一份全局變量的單獨的拷貝。
為了理解線程間的通信,我們有必要知道一個對象生存的地方(具體在哪個STA套間)。和一個工作者對象進行任何的通信都總是被封裝傳送到工作者對象所在的對應套間。每個STA套間都有它自己的線程,而且這個線程是 唯一一個可以讓存在于這個套間的任意對象執(zhí)行代碼的線程。
總之,Thread Factory提供了一個較全面的解決方案來用于創(chuàng)建健壯的多線程Visual Basic應用程序。我們建議你把 指南 部份詳細閱讀一遍,它會引導你一步一步地完成創(chuàng)建工作者對象和對他們的異步調用的整個過程。
Thread Factory™是一個用于讓程序員能非常容易的創(chuàng)建一個健壯的Visual Basic多線程應用程序的一個DLL組件庫。Thead Factory使得Visual Basic程序員們能創(chuàng)建出運行在另外的線程的ActiveX對象。Thead Factory也包含一個較完癢的用來調用和取消異步調用的框架。在Thead Factory眾多著名的特點當中,最顯著的是它的健壯性、性能表現和易用性。Thead Factory遵循COM的所有規(guī)則,而且避免使用任何繞過COM組件的技術。和試圖通過創(chuàng)建另外的進程來實現模擬多線程的ActiveX EXE服務器的產品不同,Thread Factory™會創(chuàng)建真正運行在同一個進程內的多線程VB6應用程序和組件。以下是兩種架構的一個比較。
任務和特點 | Thread Factory™ | ActiveX EXE |
初始化與啟動 | 快 (毫秒級) | 慢 (通常5-15秒) |
對象創(chuàng)建+Marshalling | 快 (進程內,In-Process) | 非常慢 (進程外,Out-Of-Process) |
支持在單個VB6 IDE會話中調試 | 是 | 否 |
內置異步調用 | 是 | 否 |
內置異步錯誤處理 | 是 | 否 |
內置取消異步調用操作 | 是 | 否 |
支持創(chuàng)建多線程OCX 控件 | 是 | 否 |
要求實現特定的接口 | 否 | 是 |
共享進程特定資源( hDC、hWnd、內存地地址) | 是 | 否 |
總之,Thread Factory™允許開發(fā)者快速而經濟的在所有微軟Windows操作系統(tǒng)上創(chuàng)建結實而快速的多線程應用程序和組件。
新的 AsyncControl OCX控件
支持通過微軟件的Excel和Access來創(chuàng)建真正的多線程VBA應用程序
兼容.NET
新的 OnCancel 事件
新的 AsyncCall 方法,用于簡化異步調用
新的 輔助函數類,詳見 TFHelperFunctions
加強的在線文檔,包含修訂過的 程序員筆記 部份
加強的Win32 API 定義,詳見 Win32 API 調用
可安全使用VB6的IDE進程調試
支持可使COM+平衡化的異步調用(Begin_ 和 End_)
加強的異步調用錯誤處理
包含StopWatchPro™(譯者注:秒表)多線程OCX控件(現在支持VBA)
新的 ElapsedTime 類——高精度定時(精確到1毫秒)
支持取消異步調用操作,詳見 CancelObject
支持創(chuàng)建多線程OCX控件,詳見 多線程OCX控件示例
支持完全可配置的線程優(yōu)先級機制,詳見 SetThreadPriority
新的 ThreadHANDLE 和 ThreadID 屬性
新的運行時模塊(大小共160K),詳見 發(fā)布應用程序
支持輕松訪問有用的Win32 API函數和同步對象
Thead Factory組件庫使用C++(ATL)和匯編語言開發(fā)。
備注: Thead Factory不依賴COM+或者任何MFC組件。
Thead Factory兼容COM技術,并且能夠被如微軟的VB6、Excel、Access和.NET等不同的支持COM組件技術的開發(fā)工具支持。程序員如果是使用像Excel、Access或者.NET這樣的IDE環(huán)境來開發(fā)程序的話,應當使用代理對象設計模式來進行設計。
|站長郵箱|小黑屋|手機版|Office中國/Access中國
( 粵ICP備10043721號-1 )
GMT+8, 2025-7-13 03:10 , Processed in 0.069947 second(s), 18 queries .
Powered by Discuz! X3.3
© 2001-2017 Comsenz Inc.