設(shè)為首頁收藏本站Access中國

Office中國論壇/Access中國論壇

 找回密碼
 注冊

QQ登錄

只需一步,快速開始

tag 標簽: access

相關(guān)帖子

版塊 作者 回復/查看 最后發(fā)表
忘掉所有的VBA,一天完成二十個功能點 Access常規(guī)交流 李嘯林 2006-6-6 12 8032 WFH6898 2015-11-22 10:49
[源碼]關(guān)閉所有窗體的自動糾正功能 Access源碼 tmtony 2012-12-13 3 4045 yanwei82123300 2012-12-13 16:27
學習access的十大“捷徑” 課件+標準化命名規(guī)則+Access學習線路圖 打包下載 attachment Office在線培訓 admin 2013-6-1 416 60778 xxk8077 2023-3-5 20:27
活動 第14堂-office的程序如何發(fā)布打包[Office公開課免費培訓] attach_img Office在線培訓 admin 2013-7-11 0 10623 admin 2013-7-11 21:20
全局置頂 Access通用開發(fā)平臺標準版免費下載 attach_img Access開發(fā)平臺發(fā)布 tmtony 2014-1-30 230 289304 qetuadgj11 2024-10-25 16:51
Access網(wǎng)絡(luò)版數(shù)據(jù)庫開發(fā)--ADP的使用 學習路線 Office在線培訓 admin 2014-5-9 5 17031 jlf001 2016-4-10 15:21
Office中國服務(wù) attach_img Office中國公告 admin 2015-1-2 0 2440 admin 2015-1-2 15:36
活動 Access實名交流群資料審核 Access/Office網(wǎng)店 admin 2015-1-23 0 3012 admin 2015-1-23 15:08
access通用平臺打開main進不去,提示宏錯誤的解決辦法 attach_img Access開發(fā)平臺交流 admin 2015-3-28 2 3583 purplerose 2016-4-2 22:13
【技巧】WebBrowser瀏覽器控件的相關(guān)方法、屬性、事件 Access技巧 tmtony 2015-4-17 3 4774 李力軍2 2016-5-10 12:30
【源碼】最全的有關(guān)剪切板(ClipBoard)的各種操作和相關(guān)源代碼集錦 Access源碼 tmtony 2015-4-18 0 3329 tmtony 2015-4-18 09:13
ADP 項目開發(fā)培訓教程--Lwwvb版主主講 Access技巧 admin 2015-10-14 1 2971 34acc 2015-10-14 16:35
Access培訓一攬子解決方案-Access中級培訓 高級培訓 更高級培訓重新改版,全新亮相 Access技巧 admin 2015-10-15 1 4431 admin 2015-10-15 14:20
新春大優(yōu)惠 Office中國全線產(chǎn)品降價促銷 Office中國公告 admin 2015-12-31 0 5896 admin 2015-12-31 12:00
完美報表解決方案(報表設(shè)計高級培訓) attach_img Office在線培訓 admin 2016-4-2 2 6383 admin 2016-4-5 12:29
娛樂而已——excel進度條 attachment Excel VBA開發(fā) roych 2017-3-22 1 4792 liwen 2017-3-22 14:39

相關(guān)日志

分享 Access菜鳥七大邪門武器之五:在access中使用多線程(三)
熱度 1 ganlinlao 2016-4-3 10:44
Access菜鳥七大邪門武器之五:在access中使用多線程(三)
在這里我將萬分感謝網(wǎng)友西門吹雪。如果不是他的辛苦和努力,我們很難想到在Access中用上多線程,至少在此之前,我從沒想過!皠e人的一小步,我們節(jié)省很長的一段路”。西門吹雪,謝謝你。 VB/COM 多線程概述 多線程程序 Visual Basic和COM Thread Factory 異步調(diào)用 錯誤處理 調(diào)試 封裝傳送 ActiveX EXE服務(wù)器模型 同步和線程安全 TLS (Thread Local Storage,線程本地存儲)     這篇指南提供了一篇關(guān)于使用Visual Basic和Thread Factory開發(fā)多線程COM應用程序的概述。它假設(shè)讀者對COM和COM線程模型(包括COM STA )已經(jīng)有一定的了解。其中不會包含MTA(Multi Threaded-Apartment,多線程套間),主要是因為在Visual Basic不能創(chuàng)建MTA對象。   一個 多線程程序 是一個可以在同一個應用程序當中同時執(zhí)行2個或者更多的任務(wù)的程序 ,多任務(wù)是通過分別指派一個單獨的線程來執(zhí)行一個任務(wù)來實現(xiàn)的。一個線程基本上就是一個程序代碼執(zhí)行的一個路徑,同時它也是在Win32調(diào)度器當中可以識別的最小的執(zhí)行單元。一個進程由一個或者多個線程以及程序的代碼、數(shù)據(jù)和程序中其他在內(nèi)存 中的資源組成。Windows的調(diào)度器決定哪個線程應該運行以及什么時候運行。在多處理器計算機上,調(diào)度器可以通過切換單個線程到不同的處理器上來平衡CPU的負載。   在使用得當?shù)那闆r下,多線程技術(shù)可以明顯提高一個應用程序的性能和反應速度,但如果使用不當,它也可以令一個程序變慢。經(jīng)驗是設(shè)計多線程程序的最好的老師,沒有硬性規(guī)定什么時候應該使用多線程技術(shù)?梢酝ㄟ^仔細檢查程序 ,來看看到底將哪個任務(wù)單獨放到另外的線程中運行會更有用,又或者檢查看看哪些任務(wù)如果同時運行會使程序更好,以上兩點都是比較好的切入點。   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對象也會接管所有和這個工作者對象有關(guān)的來 往傳輸?shù)腃OM調(diào)用、異步調(diào)用和異步調(diào)錯誤處理。每個工作者對象都被一個單獨的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 關(guān)鍵字來創(chuàng)建一個其他的新的工作者對象,那這個工作者對象會和新創(chuàng)建的工作者對象共享同一個套間和線程。   如果關(guān)閉應用程序的主線程(主STA),則會引起所有的工作者都同時關(guān)閉。   要想產(chǎn)生一個真正的多線程程序,通過在一個另外的線程中運行工作者對象這樣的方式這是不夠的。因為所有的Visual Basic COM調(diào)用都是同步的,這使得兩個線程不可能同時運行(除非你使用定時器來欺騙并且繞過COM)。AsyncObject對象公開了一個稱之為 IBlindDelegator 的特殊接口 ,這個接口使得要實現(xiàn) 異步調(diào)用變 更得更容易。這個IBlindDelegator接口會通過Begin_和Finish_方法來平行化COM+異步調(diào)用。   我們應用異步調(diào)用時,需要使用特殊的 錯誤處理 方式。錯誤一般是通過調(diào)用堆棧來返回的,而由異步調(diào)用引起的錯誤會被丟棄,這是因為那時的調(diào)用堆棧是空的,而那時 我們沒有一個可以將這個錯誤返回的上層函數(shù)。由于所有由Thread Factory創(chuàng)建的工作者對象都在AsyncObject對象中包裝著,AsyncObject對象會攔截所有在異步調(diào)用時所產(chǎn)生的錯誤并且將他們轉(zhuǎn)換為AsyncObject對象的OnError事件,這樣他們就不會被丟棄了。   在應用程序的開發(fā)過程中,最重要的步驟之一就是 調(diào)試 。Thread Factory通過允許你安全的使用Visual Basic的調(diào)試器,從而使調(diào)試變得簡單。由于Visual Basic的調(diào)試器是為單線程應用程序的調(diào)試而設(shè)計的,在調(diào)試不同的多線程應用程序的項目時,都會有一些不同的限制。   當調(diào)試一個不包含ActiveX DLL源代碼的工程時: 工作者對象會在一個另外的線程它自身的STA中創(chuàng)建,但這時這個被調(diào)試的工程當中產(chǎn)生的所有對于這個工作者對象的調(diào)用都會被串行化(即使使用IBlindDelegator接口也是這樣)。 當調(diào)試一個包括ActiveX DLL源代碼的工程時: Visual Basic會強制所有的工作者對象都在同一個主STA線程中創(chuàng)建。調(diào)試這一類項目是和調(diào)試一個普通的單線程應用程序是一模一樣的。 提 示: 微軟的Visual C++調(diào)試器是一個非常好的用來代替Visual Basic的調(diào)試器的代替品。不像VB的調(diào)試器,Visual C++的調(diào)試器設(shè)計時就有考慮到多線程程序的調(diào)試。要使用Visual C++來調(diào)試VB的應用程序和組件,你首先必須要編譯項目時勾選“產(chǎn)生符號化調(diào)試信息”選項。   備 注: 當你運行完一個多線程的工程后關(guān)閉Visual Basic的IDE時,可能會引發(fā)一個異常(見下圖)。這是因為可能在調(diào)試時我們曾經(jīng)以按下“ 停止 ”按鈕的方式來中斷調(diào)試過程,又或者在程序代碼中包含了 END 語句 ,并且試圖以它來中斷程序的運行。這會導致Visual Basic沒有正常讓清理代碼執(zhí)行,從而出現(xiàn)了下面的異常。這個異常只會在你關(guān)閉IDE時出現(xiàn), 絕對不會在運行編譯后的程序時出現(xiàn) 。一個盡量減少這種偶然錯誤的 方法是,在另外的DLL的工作者對象它們的創(chuàng)建和銷毀兩個事件過程中加入相應的代碼來處理這些工作。這樣之所以能解決問題,是因為類的Terminate事件它不管我們在什么樣的情況下退出程序 ,它都會被執(zhí)行。Progress示例程序就體現(xiàn)了這點(譯者注:這一段我沒有按照完全原文逐字翻譯,而按照我的理解來譯)。     當要和一個另外的線程或者進程通信時是會用到 封裝傳送 的。封裝傳送是在進行超出線程或進程邊界的方法調(diào)用時,完成打包和發(fā)送的行為 的體現(xiàn)。COM使用 Proxy 和 Stub 來封裝傳送那些在線程/進程間的調(diào)用。   在由Thread Factory所創(chuàng)建的工作者對象之間通信速度是非常快的,這是因為這些工作者對象都是在同一個進程里的不同線程間運行的(In-Process,進程內(nèi))。 而 ActiveX EXE 服務(wù)器模型 雖然也可以創(chuàng)建工作者對象,但是 在它創(chuàng)建的進程之間通信非常慢,這是因為在調(diào)用外部進程時會產(chǎn)生額外的針對調(diào)用的封裝傳送工作(Out-Of-Process,進程外)。   下邊的插圖展示了在Thread Factory和ActiveX EXE服務(wù)器模型兩種模型中,產(chǎn)生與其他工作者對象通信的情況時涉及的封裝傳送流程。 Thread Factory Active X EXE 服務(wù)器模型   盡管Thread Factory和ActiveX EXE服務(wù)器模型兩種模型都是創(chuàng)建工作者對象的有效方法,但是Thread Factory提供很多ActiveX EXE服務(wù)器模型所沒有的優(yōu)勢,包含: 性能:使用Thread Factory的通信速度快很多 內(nèi)置異步調(diào)用架構(gòu)(包含取消調(diào)用的操作) 內(nèi)置異步錯誤處理 異步調(diào)用超時及強制結(jié)束處理 支持單獨檢查每個工作者對象的狀態(tài) 很容易在一個單獨的Visual Basic會話中調(diào)試 支持處理特定的資源,比如UDTs(譯者注:用戶自定義結(jié)構(gòu)體)、hDC句柄、臨界區(qū)域和hEvents句柄 支持創(chuàng)建多線程OCX組件 同步 問題 在開多線程應用程序時非常重要 ,這是因為它可以保證線程的安全。我們通常使用同步對象來防止多個線程在同時訪問同一個變量或者系統(tǒng)資源時發(fā)生沖突問題。Thread Factory所創(chuàng)建的工作者對象,由COM所提供的一個特性來解決同步問題,這個特性主要指COM強制所有的工作者對象都必須在自身的STA中創(chuàng)建,并且COM串行化所有 外部對它們的訪問(譯者注:這一句是按照我自身的理解調(diào)整翻譯的,沒有完全按照原文一字一詞的譯)。串行化調(diào)用天生就很安全,這是因為在給定的任意時刻只有內(nèi) ,所有的對于對象的調(diào)用當中,只能有單個方法被執(zhí)行。如果兩個工作者對象試圖同時調(diào)用同一個另外的工作者對象的話,則兩者的調(diào)用都會被串行化,由此,只有其中一個工作者對象會得到他們想要的 ,而另一個工作者對象的操作則必須等到第一個調(diào)用操作執(zhí)行完成后才會被執(zhí)行。 很多時候在非線程安全的情況下,你必須手動添加代碼以防止發(fā)生同時對一個資源或者對象的訪問沖突問題。在一個多線程程序中資源或者對象不是線程安全的原因有很多,但通常通過串行化訪問可以使得它 們變成線程安全的。這可以通過使用Windows提供的眾多同步對象當中的某一種來實現(xiàn)。為了方便起見,Thread Factory在它的類庫中提供了一些關(guān)于臨界區(qū)域(Critical Sections)和互斥量(Mutexes) 的API調(diào)用聲明(譯者注:你可以直接在VB中使用這些技術(shù)而且不需要額外的聲明它們)——詳見 Win32 API調(diào)用 。   在開發(fā)多線程應用程序時全局變量是另外一個問題,這是因為全局變量是暴露的,這使得所有的對象(包括工作者對象)都可以同時訪問它。Thread Factory的工作者對象使用 TLS (Thread Local Storage,線程本地存儲) 技術(shù),來每為個線程都保存一份全局變量的單獨的拷貝。   為了理解線程間的通信,我們有必要知道一個對象生存的地方(具體在哪個STA套間)。和一個工作者對象進行任何的通信都總是被封裝傳送到工作者對象所在的對應套間。每個STA套間都有它自己的線程,而且這個線程是 唯一一個可以讓存在于這個套間的任意對象執(zhí)行代碼的線程。   總之,Thread Factory提供了一個較全面的解決方案來用于創(chuàng)建健壯的多線程Visual Basic應用程序。我們建議你把 指南 部份詳細閱讀一遍,它會引導你一步一步地完成創(chuàng)建工作者對象和對他們的異步調(diào)用的整個過程。 什么是Thread Factory™ ? Thread Factory™是一個用于讓程序員能非常容易的創(chuàng)建一個健壯的Visual Basic多線程應用程序的一個DLL組件庫。Thead Factory使得Visual Basic程序員們能創(chuàng)建出運行在另外的線程的ActiveX對象。Thead Factory也包含一個較完癢的用來調(diào)用和取消異步調(diào)用的框架。在Thead Factory眾多著名的特點當中,最顯著的是它的健壯性、性能表現(xiàn)和易用性。Thead Factory遵循COM的所有規(guī)則,而且避免使用任何繞過COM組件的技術(shù)。和試圖通過創(chuàng)建另外的進程來實現(xiàn)模擬多線程的ActiveX EXE服務(wù)器的產(chǎn)品不同,Thread Factory™會創(chuàng)建真正運行在同一個進程內(nèi)的多線程VB6應用程序和組件。以下是兩種架構(gòu)的一個比較。 任務(wù)和特點 Thread Factory™ ActiveX EXE 初始化與啟動 快 (毫秒級) 慢 (通常5-15秒) 對象創(chuàng)建+Marshalling 快 (進程內(nèi),In-Process) 非常慢 (進程外,Out-Of-Process) 支持在單個VB6 IDE會話中調(diào)試 是 否 內(nèi)置異步調(diào)用 是 否 內(nèi)置異步錯誤處理 是 否 內(nèi)置取消異步調(diào)用操作 是 否 支持創(chuàng)建多線程OCX 控件 是 否 要求實現(xiàn)特定的接口 否 是 共享進程特定資源( hDC、hWnd、內(nèi)存地地址) 是 否   總之, Thread Factory™ 允許開發(fā)者快速而經(jīng)濟的在所有微軟 Windows 操作系統(tǒng)上創(chuàng)建結(jié)實而快速的多線程應用程序和組件。   新特色: 新的 AsyncControl OCX控件 支持通過微軟件的 Excel 和 Access 來創(chuàng)建真正的多線程 VBA 應用程序 兼容.NET 新的 OnCancel 事件 新的 AutoClose Logic 新的 AsyncCall 方法,用于簡化異步調(diào)用 新的 輔助函數(shù)類,詳見 TFHelperFunctions 新的 GetThreadPriority 和 SetThreadPriority 方法 加強的在線文檔,包含修訂過的 程序員筆記 部份 加強的Win32 API 定義,詳見 Win32 API 調(diào)用 一般特點: 可安全使用VB6的IDE進程調(diào)試 支持可使COM+平衡化的異步調(diào)用(Begin_ 和 End_) 加強的異步調(diào)用錯誤處理 包含 StopWatchPro ™(譯者注:秒表)多線程OCX控件(現(xiàn)在支持VBA) 新的 ElapsedTime 類——高精度定時(精確到1毫秒) 支持取消異步調(diào)用操作,詳見 CancelObject 支持創(chuàng)建多線程OCX控件,詳見 多線程OCX控件示例 支持完全可配置的線程優(yōu)先級機制,詳見 SetThreadPriority 新的 ThreadHANDLE 和 ThreadID 屬性 新的運行時模塊(大小共160K),詳見 發(fā)布應用程序 支持輕松訪問有用的Win32 API函數(shù)和同步對象   架構(gòu): Thead Factory組件庫使用C++(ATL)和匯編語言開發(fā)。 備 注: Thead Factory不依賴COM+或者任何MFC組件。 Thead Factory兼容COM技術(shù),并且能夠被如微軟的VB6、Excel、Access和.NET等不同的支持COM組件技術(shù)的開發(fā)工具支持。 程序員如果是使用像Excel、Access或者.NET這樣的IDE環(huán)境來開發(fā)程序的話,應當使用代理對象設(shè)計模式來進行設(shè)計。
個人分類: access入門|8827 次閱讀|1 個評論
分享 Access菜鳥七大邪門武器之五:在access中使用多線程(二)
ganlinlao 2016-4-2 17:28
第二部分,理解com的套間線程 COM套間 為了理解com是如何處理線程的,讀者需要掌握套間的概念!套間在應用中是一個邏輯容器,以使com對象可以分享遵循相同的線程訪問規(guī)則(例在所屬線程中,規(guī)則限定了對象的方法和屬性在有套間和無套間情形中是如何被調(diào)用的)。套間本質(zhì)上只是一個概念,不像對象有自己的屬性和方法。沒有句柄類型可以引用它,更沒有可調(diào)用的API操縱它。對于新手來說,這或許是套間難理解的一個重要原因,它是如此的抽象。 如果存在CoCreateApartment()和一些諸如CoEnterApartment()的API函數(shù),如果微軟提供帶有IApartment接口和操縱線程、對象方法的COM類,套間也許會很容易理解。從編程來說,似乎沒有切實的方式去洞察套間。為了幫助新手克服剛開始學習套間遇到的困難,提供以下幾點參考: 1、套間由應用創(chuàng)建,沒有直接創(chuàng)建或者檢查它們存在的函數(shù)。 2、線程和對象也通過應用進入套間并且參與套間相關(guān)的活動,沒有現(xiàn)成的函數(shù)完成這一過程。 3、套間模型十分像協(xié)議,或者需遵循的規(guī)則集合。 在多線程訪問眾多com對象的操作系統(tǒng)中,我們?nèi)绾文鼙WC一個線程對com對象屬性、方法的調(diào)用結(jié)果不受另一線程對該對象訪問的影響?為了解決上述問題,com套間應運而生,它的出現(xiàn)就是為了保證所謂的現(xiàn)線程安全。通過套間,我們就可以保證本線程訪問對象的內(nèi)部狀態(tài)免受其它線程訪問的影響。 com中包含三種套間模型:單線程套間、多線程套間和中性套間(Neutral Apartment),每一類型套間代表了一種跨線程同步對象內(nèi)部狀態(tài)的機制。對于線程和對象,套間遵循以下的基本原則: 1、一個com對象僅且只能存在于一個套間。運行時對象一經(jīng)創(chuàng)建就確定所屬套間,并且直到銷毀它一直存在于這個套間。 2、一個com線程(內(nèi)部創(chuàng)建了 com對象或者調(diào)用了com方法的線程)也屬于一個套間。與com對象一樣,線程從創(chuàng)建到結(jié)束都存在于同一套間。 3、屬于相同套間的線程和對象遵循相同的線程訪問規(guī)則。套間內(nèi)部方法的調(diào)用是直接完成的,不需要com額外的輔助。 4、不同套間的線程和對象遵循了不同的線程訪問規(guī)則?缣组g方法調(diào)用通過列集實現(xiàn),這就需要采用proxies和stubs。 除了保證線程安全,套間的另一好處是透明性,對象和客戶端都不需要關(guān)心他們采用的套間模型。底層的套間實現(xiàn)細節(jié)(特別是列集機制)由com子系統(tǒng)管理,開發(fā)者無需關(guān)心。 (二)COM對象套間模型設(shè)定 從本段開始一直到“EXE COM服務(wù)器和套間”,期間涉及的com對象都是在DLL服務(wù)器中實現(xiàn)。正如上面提到的,com對象僅屬于一個運行的套間,在對象創(chuàng)建時就已經(jīng)確定。然而首先需要明白的是一個com對象是如何關(guān)聯(lián)它的套間模型的?對于DLL服務(wù)器中的com類,當com線程實例化它時,該類會參考注冊表“InProcServer32”字段項“ThreadingModel”的字符串值。這個設(shè)置有開發(fā)者控制。比如,當你使用ATL開發(fā)com對象時,你可以指定對象在運行時采用的線程模型。下面列舉了線程模型字符串值和代表的套間模型: 序號 注冊表值 套間模型 1 “Apartment” 單線程套間 2 “Single”或者空 Legacy單線程套間 3 “Free” 多線程套間 4 “Neutral” Neutral套間 5 “Both” 創(chuàng)建線程的套間模型 “Both”字符串值說明com對象適用于單線程套間和多線程套間。 (三)COM線程套間模型設(shè)定 每一個com線程必須通過CoInitializeEx()函數(shù)并且設(shè)定參數(shù)為COINIT_APARTMENTTHREADED或者COINIT_MULTITHREADED進行自身初始化。訪問了CoInitializeEx()函數(shù)的線程即為com線程,也即線程已經(jīng)進入套間。直到線程調(diào)用CoUninitialize()函數(shù)或者自身終止,才會離開套間。 中性線程套間(NTA) Windows 2000引入了NTA用于性能優(yōu)化。進行跨套間方法調(diào)用時,進入STA和MTA的方法調(diào)用引起的線程切換會占用大量開銷。而進入NTA的調(diào)用不會引起線程切換。如果STA或者MTA線程調(diào)用同一個進程中基于NTA的對象,線程會暫時離開其套間,直接執(zhí)行NTA中的代碼。 注:因為在某些場合下,我們很有可能會遭遇到NTA的com組件,所以適當留意一下。 單線程套間(STA) 注:因為VBA只能創(chuàng)建和使用單線程套間,所以理解單線程套間是重點。 單線程套間只能包含一個線程(因此才稱為單線程套間),但是可以包含多個對象。包含在單線程套間中的線程有一特別之處——如果線程中的對象需要導出給其它線程,那么它必須有自己的消息循環(huán)。線程通過調(diào)用CoInitializeEx()并指定函數(shù)參數(shù)為COINIT_APARTMENTTHREADED或者簡介的調(diào)用CoInitizlize()函數(shù)(CoInitialize()函數(shù)實際上會調(diào)用參數(shù)設(shè)定為COINIT_APARTMENTTHREADED的CoInitializeEx())進入單線程套間。進入單線程套間的線程也可認為已經(jīng)創(chuàng)建了那個套間(畢竟,在套間內(nèi)沒有其它線程第一次創(chuàng)建它)。通過指定“Apartment”到注冊表合適位置和在單線程套間內(nèi)實例化對象,我們可以說com對象進入單線程套間。 (一)STA線程訪問規(guī)則 STA的線程訪問規(guī)則如下: 1、所有的ST A 對象如STA線程一樣屬于相同的單線程套間。 2、STA內(nèi)的所有對象只接受該STA線程的方法調(diào)用。 第一點理解起來十分自然和簡單。然而,請注意在相同DLL服務(wù)器、不同STA線程中創(chuàng)建的同一com類的兩個對象屬于不同的套間。訪問這兩個對象輸入跨套間,必須通過com的列集完成。至于第二點,有兩種方式可以訪問STA對象: 1、STA線程內(nèi)部訪問。這種情形方法的調(diào)用被序列化。2、跨線程訪問(也即跨套間)。這種情形下STA線程必須包含消息循環(huán),COM才能保證對象僅接受自身STA線程的方法訪問 。 (二)STA中的消息循環(huán) 重要規(guī)則: STA線程需要消息循環(huán) 如果不理解單線程套間機制,這條規(guī)則看起來不那么明顯?蛻粽{(diào)用基于STA的對象時,調(diào)用將被傳遞到STA中運行的線程。COM通過向STA的隱藏窗口投遞消息來完成這種傳遞。那么,如果STA中的線程不接收和分發(fā)消息將發(fā)生什么?調(diào)用將在RPC通道中消失,永遠也不返回。它將永遠凋謝在STA的消息隊列中。 擁有消息循環(huán)的線程是UI線程,它關(guān)聯(lián)著一個或多個窗口,即線程擁有這些窗口。窗口對應的窗口過程由所屬線程創(chuàng)建。任何線程發(fā)送或者發(fā)布消息給所有的窗口,但是只有目標窗口過程才會響應這些消息。發(fā)往目標窗口的消息被同步,即窗口會保證按照消息發(fā)送或發(fā)布的順序接受處理消息。對于Windows程序開發(fā)者來說,窗口處理過程不必是線程安全的。每一個窗口消息會轉(zhuǎn)化成原子操作,只有當前消息處理完才能處理下一消息。在Windows系統(tǒng)中,這給com帶來與生俱來的優(yōu)點:使得com對象輕松的實現(xiàn)線程安全。COM通過發(fā)布私有消息給對象關(guān)聯(lián)的隱藏窗口,實現(xiàn)外部套間對STA對象方法的調(diào)用。隱藏窗口的窗口過程安排處理對象的訪問并且把結(jié)果返回調(diào)用者。 需要注意的是當涉及到外部套間時,COM總是引入porxies和stubs,如消息循環(huán)一樣兩者形成了單線程套間協(xié)議的一部分。有兩個重要的知識點需要關(guān)注: 1、系統(tǒng)只有當訪問來自外部套間時,采用消息循環(huán)激發(fā)STA對象的方法才是可使用的。如果是來自套間內(nèi)的訪問,完全不需要com的參與,STA線程自身會保證調(diào)用以串行方式執(zhí)行。 2、如果STA線程從消息隊列中獲取或分發(fā)消息失敗,線程套間內(nèi)的com對象將無法接受套間之間的訪問。 考慮到上述第2點,諸如Sleep()、WaitForSingleObject()、WaitForMultipleObjects()等影響線程消息處理順序的函數(shù)是非常重要的。如果STA線程需要等待同步對象,為了保證消息循環(huán)不被打亂,必須采取特殊處理。 實際應用中某些情形STA線程不需要包含消息循環(huán)。 (三)STA優(yōu)缺點 使用STA最大優(yōu)點就是簡潔。對于com對象服務(wù)器來說,除了一些基本的代碼,參與的com對象和線程幾乎不需要同步代碼。com中所有方法的調(diào)用會自動串行。因STA對象總是由相同線程訪問,即具有線程相似性。正是線程相似性,使得STA對象開發(fā)者能夠采用線程局部存儲保存對象內(nèi)部數(shù)據(jù)的狀態(tài)。VB和MFC采用這種開發(fā)技術(shù),所以它們開發(fā)的對象是單線程套間對象。在需要支持legacy com組件的情形中采用STA是不可避免的。 任何事物都有兩面性,當然使用STA也不例外點。 多個線程訪問同一com對象時STA架構(gòu)嚴重降低了性能。 由于每一線程訪問對象都會被串行化,所以線程必須等待其他占用線程的返回。等待時間降低了應用響應或者性能。如果線程包含過多的對象時,STA架構(gòu)也會降低性能。 切記單線程套間包含一個線程和一個消息隊列,因而STA內(nèi)各個對象的訪問由消息隊列串行化。 鑒于STA的優(yōu)缺點,使用時必須根據(jù)實際應用場景來選擇。 (四)STA COM對象及其服務(wù)器實現(xiàn) 對于開發(fā)者,STA com對象的實現(xiàn)一般不必關(guān)心內(nèi)部成員數(shù)據(jù)的串行化訪問。然而,單線程套間本身無法確保com DLL服務(wù)器的全局數(shù)據(jù)和導出的全局函數(shù)(如DllGetClassObject()和DllCanUnloadNow())線程安全。切記com服務(wù)器對象可以在任何線程中創(chuàng)建,相同Dll服務(wù)器中的兩個對象可以在各自獨立的STA線程中創(chuàng)建,這保證了無需com的串行化機制服務(wù)器的全局數(shù)據(jù)和函數(shù)可以由兩個不同線程正確訪問。 另外,這種情形下線程的消息循環(huán)也無法提供任何幫助,畢竟這不是對象的內(nèi)部狀態(tài)(如果是程序就要付出代價啦),而是服務(wù)器內(nèi)部狀態(tài)。由于不同線程的對象可能訪問服務(wù)器的全局變量和全局函數(shù),因而它們需要適當串行化。這一規(guī)則也適用于類的靜態(tài)成員函數(shù)和靜態(tài)成員變量。 一個眾所周知的com服務(wù)器變量是全局對象計數(shù),其由常用的全局函數(shù)DllGetClassObject()和DllCanUnloadNow()調(diào)用。API函數(shù)InterlockedIncrement()和InterlockedDecrement()可以同步不同線程對全局對象計數(shù)的訪問。 總結(jié)STA服務(wù)器DLL實現(xiàn)的一般原則: 1、服務(wù)器dll必須有線程安全的標準入口函數(shù),比如函數(shù)DllGetClassObject()和DllCanUnloadNow()。 2、服務(wù)器dll的私有全局函數(shù)必須是線程安全的。 3、私有全局變量(特別是全局對象計數(shù))必須是線程安全的。 DllGetClassObject()函數(shù)功能是提供類對象的訪問。類對象根據(jù)CLSID返回,并且通過它的接口(通常是IClassFactory)指針引用自身。DllGetClassObject()函數(shù)在API函數(shù)CoGetClassObject()內(nèi)部調(diào)用,com開發(fā)者無需直接調(diào)用。通過類對象的CLSID創(chuàng)建對象實例(由IClassFactory::CreateInstance()),我們可以把DllGetClassObject()函數(shù)視為com對象創(chuàng)建的開關(guān),請記住該函數(shù)最重要的一點:影響全局對象計數(shù)。我們調(diào)用函數(shù)DllGetClassObject(),可以獲得標志com服務(wù)DLL包含對象是否仍舊存在和發(fā)揮功效。DllGetClassObject()函數(shù)根據(jù)全局對象計數(shù)決定返回值,如果com服務(wù)器dll中沒有對象存在,調(diào)用該函數(shù)可以從內(nèi)存中卸載com服務(wù)器DLL。 函數(shù)DLLGetClassObject()和DllCanUnLoadNow()必須是線程安全的以同步全局對象計數(shù)。一般來說,當一個對象創(chuàng)建或者銷毀的時候,全局對象計數(shù)相應增加或者減少。本文沒有對私有全局函數(shù)和全局變量的線程安全進行詳細介紹,只能留給專家和經(jīng)驗豐富者去探討。 對于com服務(wù)器來說,保證線程安全不是復雜的過程,許多情形下只需要簡單的思考。上面的這些講解對于ATL COM服務(wù)器開發(fā)者已經(jīng)足夠(除了私有全局數(shù)據(jù)和全局函數(shù)的線程安全),所以他們只需要關(guān)注com對象的業(yè)務(wù)邏輯開發(fā)。 (五)STA線程實現(xiàn) STA線程通過調(diào)用CoInitialize()或者CoInitializeEx(COINIT_APARTMENTTHREADED)進行初始化自身。其次,如果線程創(chuàng)建的對象需要導出到其它線程(亦即其它套間),該線程需要提供消息循環(huán)處理com對象隱藏窗口的消息。切記com中隱藏窗口的窗口過程接受和處理這些私有消息,STA線程自身不會處理。如下的代碼示例了STA線程的框架: DWORD WINAPI ThreadProc(LPVOID lpvParamater) { /* Initialize COM and declare this thread to be an STA thread. */ ::CoInitialize(NULL); ... ... ... /* The message loop of the thread. */ MSG msg; while (GetMessage(msg, NULL, NULL, NULL)) { TranslateMessage(msg); DispatchMessage(msg); } ::CoUninitialize(); return 0; } STA線程十分像WinMain()函數(shù),實際上Windows應用的WinMain()函數(shù)也運行在線程中。你可以實現(xiàn)像WinMain()函數(shù)一樣的STA線程,即在消息循環(huán)之前創(chuàng)建窗口并保證窗口有合適的窗口過程。當然,在窗口過程中你可以創(chuàng)建和管理com對象,也可以跨套間訪問外部STA對象。如果你不想在線程中創(chuàng)建窗口,你仍然可以創(chuàng)建操縱對象并且跨套間訪問外部線程的方法。 (六)不需要消息循環(huán)的STA線程 STA線程有時是不需要消息循環(huán)的,比如線程僅僅創(chuàng)建和使用對象而不供其它套間訪問的情況。示例如下: int main() { ::CoInitialize(NULL); if (1) { ISimpleCOMObject1Ptr spISimpleCOMObject1; spISimpleCOMObject1.CreateInstance(__uuidof(SimpleCOMObject1)); spISimpleCOMObject1 - Initialize(); spISimpleCOMObject1 - Uninitialize(); } ::CoUninitialize(); return 0; } 上面的控制臺程序示例了主線程中創(chuàng)建STA而不需要消息循環(huán)。注意我們可以成功的調(diào)用Initialize()函數(shù)和Uninitialize()函數(shù),因它們在STA內(nèi)部調(diào)用不要列集和消息循環(huán)的參與。但是,如果我們調(diào)用::CoInitializeEx(NULL, COINIT_MULTITHREADED)函數(shù),main()函數(shù)變成了多線程套間,同時程序發(fā)生了以下變化: 1、Initialize()函數(shù)和Uninitialize()函數(shù)的調(diào)用需要com列集的協(xié)助。 2、com對象spISimpleCOMObject1屬于com子系統(tǒng)創(chuàng)建的默認STA套間而不是MTA套間。 3、main()線程本身仍然不要消息循環(huán),但是默認STA需要。 4、調(diào)用Initialize()函數(shù)和Uninitialize()函數(shù)需要消息循環(huán)參與。 切記如果STA線程需要消息循環(huán),一定要保證該消息循環(huán)穩(wěn)定而不受中斷的執(zhí)行。 (七)示例聚焦STA 下面我們重點講解單線程套間。采用的方法是觀察com對象方法被調(diào)用時執(zhí)行線程的ID,對于標準STA對象,此ID就是STA對象的ID。如果一個STA對象不屬于創(chuàng)建它的線程(即這個線程不是STA線程),那么該線程ID與執(zhí)行對象方法的線程ID不匹配。 標準STA 以一個簡單的例子講解標準STA。如下所示,例子包括一個簡單的STA com對象,對應的com類CSimpleCOMObject2實現(xiàn)了TestMethod1()方法。TestMethod1()顯示正在執(zhí)行線程的ID消息框,代碼如下: STDMETHODIMP CSimpleCOMObject2::TestMethod1() { TCHAR szMessage ; sprintf (szMessage, "Thread ID : 0x%X", GetCurrentThreadId()); ::MessageBox(NULL, szMessage, "TestMethod1()", MB_OK); return S_OK; } 我們通過一個簡單的測試程序?qū)嵗疌SimpleCOMObject2并調(diào)用方法TestMethod1,代碼如下: int main() { HANDLE hThread = NULL; DWORD dwThreadId = 0; ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); DisplayCurrentThreadId(); if (1) { ISimpleCOMObject2Ptr spISimpleCOMObject2; spISimpleCOMObject2.CreateInstance(__uuidof(SimpleCOMObject2)); spISimpleCOMObject2 - TestMethod1(); hThread = CreateThread ( (LPSECURITY_ATTRIBUTES)NULL, // SD (SIZE_T)0, // initial stack size (LPTHREAD_START_ROUTINE)ThreadFunc, // thread function (LPVOID)NULL, // thread argument (DWORD)0, // creation option (LPDWORD)dwThreadId // thread identifier ); WaitForSingleObject(hThread, INFINITE); spISimpleCOMObject2 - TestMethod1(); } ::CoUninitialize(); return 0; } 線程函數(shù)ThreadFunc()函數(shù)如下: DWORD WINAPI ThreadFunc(LPVOID lpvParameter) { ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); DisplayCurrentThreadId(); if (1) { ISimpleCOMObject2Ptr spISimpleCOMObject2A; ISimpleCOMObject2Ptr spISimpleCOMObject2B; spISimpleCOMObject2A.CreateInstance(__uuidof(SimpleCOMObject2)); spISimpleCOMObject2B.CreateInstance(__uuidof(SimpleCOMObject2)); spISimpleCOMObject2A - TestMethod1(); spISimpleCOMObject2B - TestMethod1(); } ::CoUninitialize(); return 0; } 線程函數(shù)中包含了展示當前線程ID消息框的函數(shù)DisplayCurrentThreadId(),其代碼如下: /* Simple function that displays the current thread ID. */ void DisplayCurrentThreadId() { TCHAR szMessage ; sprintf (szMessage, "Thread ID : 0x%X", GetCurrentThreadId()); ::MessageBox(NULL, szMessage, "TestMethod1()", MB_OK); } 上面的例子創(chuàng)建了單線程套間,通過線程ID來解釋這一過程。從main()函數(shù)開始,詳細分析如下: 1、main()函數(shù)通過調(diào)用CoInitializeEx和參數(shù)COINIT_APARTMENTTHREADED進入單線程套間。由此在main()中創(chuàng)建的STA對象屬于main()線程的一部分。 2、main()函數(shù)調(diào)用DisplayCurrentThreadId()函數(shù),此時會顯示main()線程的ID,設(shè)為thread_id_1。 3、隨后程序?qū)嵗薱om類SimpleCOMObject2,該對象和main()線程屬于相同的STA。 4、spISimpleCOMObject2的方法TestMethod1()顯示的線程ID肯定也是thread_id_1。隨后啟動了線程和線程函數(shù)ThreadFunc(),主程序調(diào)用WaitForSingleObject()等待線程函數(shù)ThreadFunc()的結(jié)束。 5、線程函數(shù)ThreadFunc()再次調(diào)用CoInitializeEx和參數(shù)COINIT_APARTMENTTHREADED,同時進入單線程套間。注意這里的STA套間是新創(chuàng)建的,不同于main()函數(shù)的STA。 6、線程函數(shù)中調(diào)用了DisplayCurrentThreadId()函數(shù),此時顯示的是ThreadFunc()線程的ID,設(shè)為thread_id_2。 7、隨后我們又創(chuàng)建了兩個com對象(spISimpleCOMObject2A和spISimpleCOMObject2B)。 8、線程函數(shù)中調(diào)用了兩個對象的方法TestMethod1()。 9、當對象spISimpleCOMObject2A和spISimpleCOMObject2B調(diào)用各自的方法時運行線程ID分別顯示。 10、它們顯示和函數(shù)ThreadFunc()相同的線程ID,即thread_id_2。 11、線程結(jié)束后放回主程序。 12、主程序中又一次調(diào)用方法TestMethod1(),當然顯示的線程ID依然是thread_id_1。 通過示例需要記住的是:同一com類的不同實例可以屬于不同獨立的STA。對于一個標準的STA模型,重要的是哪個套間實例化了該STA對象。這個例子不需要提供任何的消息徐循環(huán),因為各對象都在他們自己的套間內(nèi),并沒有跨線程調(diào)用。即使我們在main()中調(diào)用了WaitForSingleObject()也不會遇到任何麻煩。 默認STA 假如STA對象在一個非STA線程中創(chuàng)建情形會如何?下面的代碼示例了此種情況,其中com類 SimpleCOMObject2和函數(shù)DisplayCurrentThreadId()同第一個例子。 int main() { ::CoInitializeEx(NULL, COINIT_MULTITHREADED); DisplayCurrentThreadId(); if (1) { ISimpleCOMObject2Ptr spISimpleCOMObject2; /* If a default STA is to be created and used, it will be created */ /* right after spISimpleCOMObject2 (an STA object) is created. */ spISimpleCOMObject2.CreateInstance(__uuidof(SimpleCOMObject2)); spISimpleCOMObject2 - TestMethod1(); } ::CoUninitialize(); return 0; } 程序的詳細執(zhí)行過程如下: 1、main()函數(shù)調(diào)用CoInitializeEx(NULL, COINIT_MULTITHREADED),主線程初始化自身為多線程套間。 2、隨即程序調(diào)用DisplayCurrentThreadId()函數(shù),顯示了main()線程的ID。 3、接著STA對象spISimpleCOMObject2在線程中實例化。 4、注意對象spISimpleCOMObject2在非STA線程中初始化,它不屬于main()線程的MTA,屬于默認的STA。 5、調(diào)用spISimpleCOMObject2對象的方法TestMethod1(),此時顯示的線程ID不是main()線程ID。 當所有的STA對象在一個非STA線程中創(chuàng)建時,它們自動歸屬于一個默認的STA,該STA是在創(chuàng)建對象時創(chuàng)建的。這時在創(chuàng)建對象的線程中獲得是對象的代理而非指針。注意,默認的STA必須包含消息循環(huán),由com提供。 做為com套間世界的新人,必須注意:即使訪問createInstance或者CoCreateInstance函數(shù),生成的對象有可能是在另一線程實例化的。com的這種行為對于用戶是完全透明的,請注意這些小的細節(jié),特別是在調(diào)試時。 Legacy STA Legacy STA屬于默認的單線程套間,其對象屬于legacy com對象。Legacy意味著那些組件沒有任何線程的知識,這些組件必須有設(shè)置成“Single”的ThreadingModel注冊表或者其它注冊表。Legacy STA對象比較重要的一點是這些對象的實例必須在相同的STA中創(chuàng)建,它們一直存在和運行在這個legacy STA中,即使它們創(chuàng)建在以::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)初始化的線程中。 Legacy STA通常是進程中的第一個單線程套間,如果一個legacy STA對象創(chuàng)建在任何STA模型之前,一個Legacy STA由com子系統(tǒng)自動創(chuàng)建。開發(fā)legacy STA對象好處在于這些對象實例的訪問將被串行化,任何兩個legacy STA對象間的調(diào)用不要列集的參與。然而,非legacy STA的對象必須通過inter-apartment列集訪問legacy STA對象,反之也是如此。我認為這不是一個有吸引力的優(yōu)點。 另外,所有的Legacy STA對象必須僅能在相同的STA線程創(chuàng)建。 EXE COM服務(wù)器和套間 討論完Dll com服務(wù)器,為了文章的完整性接著介紹EXE COM服務(wù)器。首先介紹DLL服務(wù)器和EXE服務(wù)器的兩個重要差別。 差別1:對象創(chuàng)建的方式 當COM創(chuàng)建定義在DLL中的com對象時,必須加裝該dll,調(diào)用導出函數(shù)DllGetClassObject()獲得com對象工廠類的IClassFactory接口指針。EXE服務(wù)器實際上也遵循這個過程:獲得com對象工廠類的IClassFactory接口指針并通過它創(chuàng)建對象。那么DLL服務(wù)器和EXE服務(wù)器差別在那里? DLL服務(wù)器需要導出函數(shù)DllGetClassObject()以便com能夠提取類工廠,而EXE服務(wù)器無需導出任何函數(shù),但要在啟動時向com子系統(tǒng)注冊類工廠,退出時銷毀。這一注冊過程通過調(diào)用API函數(shù)CoRegisterClassObject()實現(xiàn)。 差別2:對象套間模型聲明的方式 本文前面提到DLL服務(wù)器中的對象通過設(shè)置“InProcServer2”注冊表中的“ThreadingModel”字符串項聲明自己的套間。EXE服務(wù)器中的對象不用設(shè)置注冊表,注冊對象類工廠的線程的套間模型決定了對象套間模型。 除了上述兩點差別,讀者還應該注意的是DLL服務(wù)器中的STA對象有可能只服務(wù)于線程內(nèi)的調(diào)用,而來客戶端對EXE服務(wù)器對象的所有方法調(diào)用都以跨線程的方式實現(xiàn),這一過程需要使用列集和stubs,以及對象所屬套間線程的消息循環(huán)。
個人分類: access入門|2578 次閱讀|0 個評論
分享 Access菜鳥七大邪門武器之五:在access中使用多線程(一)。
ganlinlao 2016-4-2 17:02
在學習VBA的過程中,有三塊內(nèi)容基本不會觸及到,1、是編譯器(和宏定義);2、指針;3、多線程 接下來我們要談的是多線程的內(nèi)容。即在vba中如何使用多線程? 第一部分 理解多線程的基本概念: 當前流行的Windows操作系統(tǒng)能同時運行幾個程序(獨立運行的程序又稱之為進程),對于同一個程序,它又可以分成若干個獨立的執(zhí)行流,我們稱之為線程,線程提供了多任務(wù)處理的能力。用進程和線程的觀點來研究軟件是當今普遍采用的方法,進程和線程的概念的出現(xiàn),對提高軟件的并行性有著重要的意義,F(xiàn)在的大型應用軟件無一不是多線程多任務(wù)處理,單線程的軟件是不可想象的。因此掌握多線程多任務(wù)設(shè)計方法對每個程序員都是必需要掌握的。 一、了解線程   要講解線程,不得不說一下進程,進程是應用程序的執(zhí)行實例,每個進程是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它系統(tǒng)資源組成。進程在運行時創(chuàng)建的資源隨著進程的終止而死亡。線程的基本思想很簡單,它是一個獨立的執(zhí)行流,是進程內(nèi)部的一個獨立的執(zhí)行單元,相當于一個子程序。單獨一個執(zhí)行程序運行時,缺省地包含的一個主線程,主線程以函數(shù)地址的形式出現(xiàn),提供程序的啟動點,如main()或WinMain()函數(shù)等。當主線程終止時,進程也隨之終止。根據(jù)實際需要,應用程序可以分解成許多獨立執(zhí)行的線程,每個線程并行的運行在同一進程中。   一個進程中的所有線程都在該進程的虛擬地址空間中,使用該進程的全局變量和系統(tǒng)資源。操作系統(tǒng)給每個線程分配不同的CPU時間片,在某一個時刻,CPU只執(zhí)行一個時間片內(nèi)的線程,多個時間片中的相應線程在CPU內(nèi)輪流執(zhí)行,由于每個時間片時間很短,所以對用戶來說,仿佛各個線程在計算機中是并行處理的。操作系統(tǒng)是根據(jù)線程的優(yōu)先級來安排CPU的時間,優(yōu)先級高的線程優(yōu)先運行,優(yōu)先級低的線程則繼續(xù)等待。   線程被分為兩種:用戶界面線程和工作線程(又稱為后臺線程)。用戶界面線程通常用來處理用戶的輸入并響應各種事件和消息,其實,應用程序的主執(zhí)行線程對象就是一個用戶界面線程,當應用程序啟動時自動創(chuàng)建和啟動,同樣它的終止也意味著該程序的結(jié)束,進程終止。工作線程用來執(zhí)行程序的后臺處理任務(wù),比如計算、調(diào)度、對串口的讀寫操作等,對它來說最重要的是如何實現(xiàn)工作線程任務(wù)的運行控制函數(shù)。工作線程和用戶界面線程啟動時要調(diào)用同一個函數(shù)的不同版本;最后需要讀者明白的是,一個進程中的所有線程共享它們父進程的變量,但同時每個線程可以擁有自己的變量。 歸納總結(jié): 什么是進程? 當一個程序開始運行時,它就是一個進程,進程包括運行中的程序和程序所使用到的內(nèi)存和系統(tǒng)資源。 而一個進程又是由多個線程所組成的。 什么是線程? 線程是程序中的一個執(zhí)行流,每個線程都有自己的專有寄存器(棧指針、程序計數(shù)器等),但代碼區(qū)是共享的,即不同的線程可以執(zhí)行同樣的函數(shù)。 什么是多線程? 多線程是指程序中包含多個執(zhí)行流,即在一個程序中可以同時運行多個不同的線程來執(zhí)行不同的任務(wù),也就是說允許單個程序創(chuàng)建多個并行執(zhí)行的線程來完成各自的任務(wù)。 多線程的好處: 可以提高CPU的利用率。在多線程程序中,一個線程必須等待的時候,CPU可以運行其它的線程而不是等待,這樣就大大提高了程序的效率。 多線程的不利方面: 線程也是程序,所以線程需要占用內(nèi)存,線程越多占用內(nèi)存也越多; 多線程需要協(xié)調(diào)和管理,所以需要CPU時間跟蹤線程; 線程之間對共享資源的訪問會相互影響,必須解決競用共享資源的問題; 線程太多會導致控制太復雜,最終可能造成很多Bug; 二、線程的管理和操作 (一)線程的啟動 (二)線程的優(yōu)先級 (三)線程的懸掛和恢復 (四)結(jié)束線程 三、線程之間的通信 通常情況下,一個次級線程要為主線程完成某種特定類型的任務(wù),這就隱含著表示在主線程和次級線程之間需要建立一個通信的通道。一般情況下,有下面的幾種方法實現(xiàn)這種通信任務(wù):使用全局變量、使用事件對象、使用消息。這里我們主要介紹后兩種方法。  。1) 利用用戶定義的消息通信 在Windows程序設(shè)計中,應用程序的每一個線程都擁有自己的消息隊列,甚至工作線程也不例外,這樣一來,就使得線程之間利用消息來傳遞信息就變的非常簡單。 對于工作者線程,如果它的設(shè)計模式也是消息驅(qū)動的,那么調(diào)用者可以向它發(fā)送初始化、退出、執(zhí)行某種特定的處理等消息,讓它在后臺完成。在控制函數(shù)中可以直接使用::GetMessage()這個SDK函數(shù)進行消息分檢和處理,自己實現(xiàn)一個消息循環(huán)。GetMessage()函數(shù)在判斷該線程的消息隊列為空時,線程將系統(tǒng)分配給它的時間片讓給其它線程,不無效的占用CPU的時間,如果消息隊列不為空,就獲取這個消息,判斷這個消息的內(nèi)容并進行相應的處理。 (2)用事件對象實現(xiàn)通信 在線程之間傳遞信號進行通信比較復雜的方法是使用事件對象,用MFC的Cevent類的對象來表示。事件對象處于兩種狀態(tài)之一:有信號和無信號,線程可以監(jiān)視處于有信號狀態(tài)的事件,以便在適當?shù)臅r候執(zhí)行對事件的操作。 四、線程之間的同步 前面我們講過,各個線程可以訪問進程中的公共變量,所以使用多線程的過程中需要注意的問題是如何防止兩個或兩個以上的線程同時訪問同一個數(shù)據(jù),以免破壞數(shù)據(jù)的完整性。保證各個線程可以在一起適當?shù)膮f(xié)調(diào)工作稱為線程之間的同步。 (1) 臨界區(qū)   臨界區(qū)是保證在某一個時間只有一個線程可以訪問數(shù)據(jù)的方法。使用它的過程中,需要給各個線程提供一個共享的臨界區(qū)對象,無論哪個線程占有臨界區(qū)對象,都可以訪問受到保護的數(shù)據(jù),這時候其它的線程需要等待,直到該線程釋放臨界區(qū)對象為止,臨界區(qū)被釋放后,另外的線程可以強占這個臨界區(qū),以便訪問共享的數(shù)據(jù)。臨界區(qū)對應著一個CcriticalSection對象,當線程需要訪問保護數(shù)據(jù)時,調(diào)用臨界區(qū)對象的Lock()成員函數(shù);當對保護數(shù)據(jù)的操作完成之后,調(diào)用臨界區(qū)對象的Unlock()成員函數(shù)釋放對臨界區(qū)對象的擁有權(quán),以使另一個線程可以奪取臨界區(qū)對象并訪問受保護的數(shù)據(jù)。同時啟動兩個線程,它們對應的函數(shù)分別為WriteThread()和ReadThread(),用以對公共數(shù)組組array[]操作 (2)互斥 互斥與臨界區(qū)很相似,但是使用時相對復雜一些,它不僅可以在同一應用程序的線程間實現(xiàn)同步,還可以在不同的進程間實現(xiàn)同步,從而實現(xiàn)資源的安全共享;コ馀cCmutex類的對象相對應,使用互斥對象時,必須創(chuàng)建一個CSingleLock或CMultiLock對象,用于實際的訪問控制,因為這里的例子只處理單個互斥,所以我們可以使用CSingleLock對象,該對象的Lock()函數(shù)用于占有互斥,Unlock()用于釋放互斥。 (3)信號量 信號量的用法和互斥的用法很相似,不同的是它可以同一時刻允許多個線程訪問同一個資源,創(chuàng)建一個信號量需要用Csemaphore類聲明一個對象,一旦創(chuàng)建了一個信號量對象,就可以用它來對資源的訪問技術(shù)。要實現(xiàn)計數(shù)處理,先創(chuàng)建一個CsingleLock或CmltiLock對象,然后用該對象的Lock()函數(shù)減少這個信號量的計數(shù)值,Unlock()反之。
個人分類: access入門|4479 次閱讀|0 個評論
分享 重返中世紀——Access菜鳥眼中的html界面庫
熱度 1 ganlinlao 2015-7-13 10:18
問題1:access使用html界面庫有沒有意義? 問題2:IE已死,還繼續(xù)使用它嗎? 問題3:access中html的使用是javascript化還是VB化? 問題4:純html的#include功能,如何實現(xiàn)? 問題5:html的數(shù)據(jù)綁定功能何在? 問題6:html如何編譯成二進制dll,有必要編譯嗎? 問題7:有沒有存在html的客戶端控件能象asp.net的服務(wù)端控件那么好用? 問題8:IE的內(nèi)存泄露會不會是一個擺不掉的惡魔問題? 問題9:html的跨域訪問問題能解決嗎?
個人分類: access入門|2600 次閱讀|1 個評論
分享 您好
sunday013 2015-6-10 00:04
初次見面,也是access初學者,請多指教
0 個評論
分享 access圖像框圖片顯示的問題
leonshi 2015-4-27 21:46
這段時間再完善主界面,在使用圖像框的時候,當在access2010開發(fā)的程序主界面圖像顯示沒有問題,后來在access2003測試出現(xiàn)了不能正常顯示的問題,后來與小寶交流,加上最近在網(wǎng)上看到一篇帖子終于找到原因,如果出現(xiàn)于我同樣的問題,首先檢查access2003版本,是否安裝了圖形篩選器,或者安裝SP2或SP3進行升級,這樣就可以解決這個問題了!
個人分類: Access本身|1219 次閱讀|0 個評論
分享 access下載,包括access2010下載,access2007下載,access2003下載等版本的下載
竹笛 2012-8-2 12:45
access下載,根據(jù)access的愛好者需要,特從網(wǎng)絡(luò)上搜索、整理了access各個版本的下載, 包括:access2010版,access2007版,access2003版,access2000版,acces97版等。 網(wǎng)址: http://www.accessoft.com/download.html
個人分類: 原創(chuàng)|2402 次閱讀|0 個評論
分享 access教程,包括access2010教程,access2007教程,access2003教程
竹笛 2012-8-2 12:44
分別有2010版本、2007版本,2003版本,共十章,由微軟最有價值專家團隊編著,從零基礎(chǔ)講解, 循序漸進、由淺入深,從表設(shè)計到報表輸出,VBA代碼設(shè)計,適合初學者系統(tǒng)地學習ACCESS編程。 在線學習網(wǎng)址: http://www.accessoft.com/tutorials.html
個人分類: 原創(chuàng)|2617 次閱讀|0 個評論
分享 小談 Access 開發(fā) (二)
zhuyiwen 2012-2-18 14:01
單一數(shù)據(jù)庫文件應用 最常見的 Access 應用程序就是在一個 Access mdb(accdb) 既包含數(shù)據(jù)存儲同時還包含應用程序(窗體、報表、宏、VBA模塊),它一個應用完整的封裝在一個數(shù)據(jù)庫文件中。這樣的好處是把一個應用封裝在一個數(shù)據(jù)庫文件中成為有機的一體,完善的體現(xiàn)了“數(shù)據(jù)庫是一個軟件”概念。不好的地方就是數(shù)據(jù)不能共享,作為一個企業(yè)應用來說就不是一個好的解決方案,這種做法比較適合個人應用。 這種應用方式最原始的動機是為了在同一個文件中處理文件中包含的數(shù)據(jù),使之成為一個有機體。 最開始的時候,我們可能通過動作查詢來維護數(shù)據(jù),比如,使用生成表查詢、追加查詢來新增數(shù)據(jù),使用刪除查詢來刪除數(shù)據(jù),使用更新查詢來更新數(shù)據(jù);使用選擇查詢、聯(lián)合查詢、交叉表查詢來察看和展示數(shù)據(jù);使用數(shù)據(jù)定義查詢來產(chǎn)生新的數(shù)據(jù)表、刪除數(shù)據(jù)表、維護數(shù)據(jù)表結(jié)構(gòu)、創(chuàng)建和維護數(shù)據(jù)表索引。 很快我們就發(fā)現(xiàn)這些手工操作,容易出錯和遺漏、顛倒數(shù)據(jù)輸入的順序、忽略數(shù)據(jù)之間的邏輯關(guān)系,并且還要編寫很多重復的查詢才能達到目的,盡管我們可以直接打開表或選擇查詢可以直接產(chǎn)生新的數(shù)據(jù)和維護數(shù)據(jù),但仍然很難達到我們的目的。并且還只有那些對數(shù)據(jù)有經(jīng)驗的人才能做到一切,很難做到數(shù)據(jù)庫對大家都有用。于是乎,這樣的數(shù)據(jù)庫對工作并沒有多大幫助,數(shù)據(jù)庫用起來還非常吃力。那么,怎么辦呢?
個人分類: Access|1124 次閱讀|0 個評論
分享 小談 Access 開發(fā) (一)
zhuyiwen 2012-2-16 23:37
小談 Access 開發(fā) (一)
Access 是 Office 的五大組件之一,而我們大多數(shù)人都說 Access 是一個關(guān)系數(shù)據(jù)庫管理系統(tǒng),而 Microsoft 卻說是“Access 應用程序”。既然這樣,那這二者的說法有什么區(qū)別呢?這很值得我們思考。 Access 是一個關(guān)系數(shù)據(jù)庫管理系統(tǒng)之說 Access 具備關(guān)系數(shù)據(jù)庫管理系統(tǒng)(DBMS)的幾乎所有特征。 1、數(shù)據(jù)存儲功能 2、數(shù)據(jù)操縱功能(Jet SQL) 3、數(shù)據(jù)管理功能(Jet SQL) 4、數(shù)據(jù)安全性 5、用戶管理功能 Access 應用程序之說 具有應用程序的開發(fā)能力。 1、簡單的宏操作 2、VBA 高級語言的執(zhí)行能力 3、完備的用戶界面(菜單、工具欄、窗體、報表)展示能力 4、其他數(shù)據(jù)庫的開發(fā)能力(ODBC、ADO、DAO、鏈接表) 5、完整應用程序開發(fā)對象(Application)及其內(nèi)部對象結(jié)構(gòu) 正是由于上述說法才形成了我們目前的 Access 應用方式,有二:一是純粹的數(shù)據(jù)管理,例如,網(wǎng)站使用 Access 數(shù)據(jù)庫作為數(shù)據(jù)的存儲媒體,以及其它高級語言開發(fā)的小型數(shù)據(jù)庫應用程序;二,使用 Access 作為開發(fā)工具,這個我們并不少見,如,Access 財務(wù)管理軟件、Access 庫存管理系統(tǒng)等...... 這是我們看到的 Access 的數(shù)據(jù)存儲功能
個人分類: Access|1140 次閱讀|0 個評論
分享 vpn
JosephTan 2012-2-15 07:13
昨天看到有網(wǎng)友討論access的上網(wǎng)問題。我想回去賣一臺服務(wù)器試一下windows server 2008r2的vpn設(shè)置
1450 次閱讀|0 個評論
分享 access使用狀況!
熱度 1 chenyingfengsx 2011-9-6 10:14
從2001年進入公司使用access2.0第一次接觸access到現(xiàn)在的access2010使用access已經(jīng)將近10年的歷程了,期間有憂愁,也有興奮。直到現(xiàn)在自己用access開發(fā)企業(yè)使用的ERP系統(tǒng),覺得access就是方便,簡單,成型快,并且解決問題也迅速!
1550 次閱讀|2 個評論

QQ|站長郵箱|小黑屋|手機版|Office中國/Access中國 ( 粵ICP備10043721號-1 )  

GMT+8, 2025-7-17 00:36 , Processed in 0.089146 second(s), 26 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

返回頂部