Office中國(guó)論壇/Access中國(guó)論壇

 找回密碼
 注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

返回列表 發(fā)新帖
查看: 9843|回復(fù): 8
打印 上一主題 下一主題

[Access本身] Access菜鳥(niǎo)七大邪門(mén)武器之五:在VBA中簡(jiǎn)單使用多線(xiàn)程

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
1#
發(fā)表于 2016-12-19 12:56:41 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
本帖最后由 ganlinlao 于 2016-12-19 13:26 編輯

       兄弟,如果你和我一樣是一個(gè)Access菜鳥(niǎo),而且是屬于無(wú)可救藥的那種,那么在過(guò)去的很多年里,你一定聽(tīng)過(guò)很多很多的人不停地告訴你,VB6、VBA是無(wú)法使用多線(xiàn)程的,以至于你忍不住相信多線(xiàn)程是VBA不可逾越的障礙。忍不住相信多線(xiàn)程對(duì)于VBA來(lái)說(shuō)只是傳說(shuō)中的詩(shī)和遠(yuǎn)方,遠(yuǎn)方遙遠(yuǎn)得一無(wú)所有。而我們這樣的菜鳥(niǎo)只剩下眼前單線(xiàn)程的茍且,并把這種茍且當(dāng)成一種美好、快樂(lè)和滿(mǎn)足。

是的,他們說(shuō)的沒(méi)錯(cuò),VBA有著一顆易碎而脆弱的玻璃心。


在過(guò)去的十多年里,無(wú)數(shù)的人都試圖在vba里使用多線(xiàn)程,但幾乎都倒在淋漓的血泊和無(wú)盡的讓人抓狂的自虐中。


今天讓我們揭開(kāi)在VBA中使用多線(xiàn)程神秘的面紗,讓這個(gè)沐浴著陽(yáng)光梳著長(zhǎng)發(fā)的美少女,能對(duì)你輕輕回眸一笑。


今天的cpu絕大多數(shù)是雙核雙線(xiàn)程、雙核4線(xiàn)程、四核四線(xiàn)程、四核八線(xiàn)程、八核……,所以在VBA中使用多線(xiàn)程來(lái)提高運(yùn)行效率顯得很有必要。而且在一些場(chǎng)景下,你也很渴望能使用多線(xiàn)程來(lái)改善慘不忍睹的狀況,比如在excel中的多個(gè)大循環(huán)計(jì)算會(huì)讓excel表很卡,大批量復(fù)制文件,下載網(wǎng)絡(luò)數(shù)據(jù)……都很容易造成accessexcel界面出現(xiàn)“假死”。


     在VBA中,其實(shí)可以使用不少的線(xiàn)程庫(kù),比如vbthreadfacoryvbrichclient5中線(xiàn)程庫(kù)……事實(shí)上你能找到不少的vb線(xiàn)程庫(kù),前面兩種線(xiàn)程庫(kù)是我了解到的比較穩(wěn)定而且功能比較完備的線(xiàn)程庫(kù)。今天我在這里介紹是另一種更加簡(jiǎn)單易用的線(xiàn)程庫(kù),雖然它功能不那么完備,但卻是簡(jiǎn)單明了,用法簡(jiǎn)單直接,能讓我們更容易理解多線(xiàn)程的概念和入門(mén)使用。

關(guān)于進(jìn)程、線(xiàn)程的概念,你可以百度或詳見(jiàn)我的空間日志。

      Accessexcel的主線(xiàn)程:Access.exe是一個(gè)進(jìn)程,access.exe會(huì)為每一個(gè)打開(kāi)的access文件(指的是客戶(hù)端)分配一個(gè)線(xiàn)程(這里我們暫且稱(chēng)此線(xiàn)程是主線(xiàn)程),這個(gè)Access文件中所有的Form,控件都在同一個(gè)線(xiàn)程中運(yùn)行。注意是所有的Form及其下面的控件!同樣的,excel.exe會(huì)為每一個(gè)打開(kāi)的workbook分配一個(gè)線(xiàn)程,這個(gè)workbook中所有的表(包括每一個(gè)表及其下面的userform及控件)都運(yùn)行在同一個(gè)線(xiàn)程中。了解這一點(diǎn)蠻重要的,因?yàn)檫@能讓我們更注意保護(hù)子線(xiàn)程的安全,減少accessexcel崩潰。

    Accessexcel的主線(xiàn)程消息處理:事件是一種消息,而消息是一種線(xiàn)性隊(duì)列,所以所有子線(xiàn)程跟主線(xiàn)程的通訊,最終都會(huì)串口化成主線(xiàn)程的事件(消息),最終在主線(xiàn)程那里變成單線(xiàn)程的事件。主線(xiàn)程無(wú)法在同一時(shí)間點(diǎn)處理兩個(gè)以上的事件,而這一點(diǎn)恰恰是accessexcel在使用多線(xiàn)程上最容易引起崩潰的原因。而且要注意,accessexcelvba處理消息能力很差(因?yàn)槲覀儾恢?/font>access處理完一個(gè)事件之后會(huì)不會(huì)在內(nèi)部自動(dòng)觸發(fā)另一個(gè)我們不知道的事件,據(jù)我所知,excel內(nèi)部事件遠(yuǎn)遠(yuǎn)比vba能用到的事件多得多),相比較而言,編譯過(guò)的vb6程序在多線(xiàn)程方面則要穩(wěn)定得多了。


      如何避免讓access在同一時(shí)間點(diǎn)處理兩個(gè)事件,是我們使用vba多線(xiàn)程排在首位任務(wù)。任何引發(fā)accessexcel在同一時(shí)間點(diǎn)處理兩個(gè)以上事件,accessexcel一定會(huì)崩潰。


      在vba中使用多線(xiàn)程,以下是我個(gè)人簡(jiǎn)單的經(jīng)驗(yàn),以后你使用多了,也可以總結(jié)一下并寫(xiě)出來(lái),讓其他人少走一些彎路。


      經(jīng)驗(yàn)1:在任何調(diào)試多線(xiàn)程代碼之前,一定要先保存。先保存后調(diào)試是鐵律,否則很多意外,會(huì)讓你欲哭無(wú)淚。


      經(jīng)驗(yàn)2:盡量減少子線(xiàn)程跟主線(xiàn)程的通訊次數(shù)。很多的時(shí)候,其實(shí)我們只需一個(gè)子線(xiàn)程跟主線(xiàn)程通訊而已(比如我們常常只需要顯示一個(gè)進(jìn)度條就夠了)。如果進(jìn)度條需要100次跟主線(xiàn)程通訊,那么想辦法讓它減少成只有10次,跟主線(xiàn)程的通訊次數(shù)越少,越能減少讓access主線(xiàn)程同時(shí)處理兩個(gè)消息的機(jī)會(huì)。


     經(jīng)驗(yàn)3:要及時(shí)主動(dòng)銷(xiāo)毀子線(xiàn)程占用的資源。不能指望accessexcel自動(dòng)幫你銷(xiāo)毀。如果子線(xiàn)程占用資源沒(méi)有銷(xiāo)毀,會(huì)引發(fā)很多問(wèn)題,甚至引起崩潰。所有一般對(duì)于子線(xiàn)程,我們盡量在需要時(shí)創(chuàng)建,用完立即銷(xiāo)毀。

    經(jīng)驗(yàn)4:如果是控件類(lèi),盡量用vb6編譯成dll,然后再在access中使用,我前面提到過(guò)了,vb6編譯過(guò)的dll在多線(xiàn)程方面要穩(wěn)定得多了。如果是寫(xiě)很多需要多線(xiàn)程調(diào)用的函數(shù),那用vb6編譯成dll,也會(huì)更穩(wěn)妥一些。

    經(jīng)驗(yàn)5:當(dāng)一個(gè)accessform不在激活狀態(tài),最好讓它對(duì)應(yīng)的子線(xiàn)程都處在休眠狀態(tài)(掛起),當(dāng)整個(gè)access客戶(hù)端,不在激活狀態(tài),也最好讓它對(duì)應(yīng)的所有子線(xiàn)程都掛起。這樣能大大減少讓access同時(shí)處理兩個(gè)消息的機(jī)會(huì)。這也同樣適用excel。不要忘了,不管是access還是excel都是mdi多文檔程序。
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享分享 分享淘帖 訂閱訂閱
2#
 樓主| 發(fā)表于 2016-12-19 12:57:05 | 只看該作者
本帖最后由 ganlinlao 于 2016-12-19 13:10 編輯

接下來(lái),你只需要足夠的耐心和謹(jǐn)慎,讓我們開(kāi)始地獄般的VBA多線(xiàn)程之旅。

VbMThread庫(kù)介紹:

方法:

Function Create() As Boolean

創(chuàng)建子線(xiàn)程

Function Terminate() As Boolean

退出當(dāng)前子線(xiàn)程

屬性:

Property hThread As Long

返回子線(xiàn)程句柄,只讀

Property Priority As ThreadPriorityConstants

設(shè)置子線(xiàn)程的優(yōu)先級(jí)。

Property Suspended As Boolean

設(shè)置子線(xiàn)程是否休眠(掛起)狀態(tài)

Property ThreadID As Long

返回子線(xiàn)程的線(xiàn)程ID,只讀

事件:

Event AsyncProcedure(SyncCallback As IThreadSyncCallback)

異步調(diào)用函數(shù)或過(guò)程或類(lèi)。

注:當(dāng)主線(xiàn)程調(diào)用子線(xiàn)程時(shí),自動(dòng)觸發(fā)此事件。

    任何在AsyncProcedure中,調(diào)用主線(xiàn)程的有關(guān)窗體、控件,都會(huì)引起崩潰。

Event  PriorityChanged(OldPriority As ThreadPriorityConstants, NewPriority As ThreadPriorityConstants)

事件:子線(xiàn)程優(yōu)先級(jí)發(fā)生變化

Event SuspendedChanged()

事件:子線(xiàn)程休眠狀態(tài)發(fā)生變化

Event SyncCallback(Argument)

事件:回調(diào)。主線(xiàn)程的回調(diào)函數(shù)。這個(gè)事件本質(zhì)是event complete()

即AsyncProcedure調(diào)用的函數(shù)運(yùn)行完后,觸發(fā)SyncCallback ,并將argument參數(shù)回調(diào)給主線(xiàn)程。

注:有關(guān)主線(xiàn)程的窗體、控件的調(diào)用或引用,都只能在SyncCallback中。也可以通過(guò)主線(xiàn)程中介,讓A子線(xiàn)程調(diào)用另一個(gè)B子線(xiàn)程,當(dāng)然也有另外的方法。

SyncCallback接口:

只有一個(gè)方法

Raise(Optional ByRef Argument As Variant)

用來(lái)告訴主線(xiàn)程,函數(shù)運(yùn)行完成。并將argument參數(shù)傳給主線(xiàn)程。



這個(gè)線(xiàn)程庫(kù),只提供異步的方式,不提供線(xiàn)程同步。事實(shí)上,在vba中,我們也只能使用異步的方式,目前我還不知道如何使用同步的方式。在大多數(shù)的情況下,使用異步多線(xiàn)程,才能比較有效地提高效率。Vbrichclient的線(xiàn)程庫(kù)同時(shí)提供了異步和同步的方式。



整個(gè)VBMThread線(xiàn)程庫(kù)原理非常簡(jiǎn)單:即創(chuàng)建一個(gè)異步子線(xiàn)程,讓子線(xiàn)程執(zhí)行一段函數(shù)或過(guò)程,函數(shù)或過(guò)程執(zhí)行完,通過(guò)argument參數(shù)將結(jié)果傳給主線(xiàn)程,然后銷(xiāo)毀子線(xiàn)程。

本線(xiàn)程庫(kù)缺失三個(gè)重要的特性:

1、缺少timeout時(shí),子線(xiàn)程強(qiáng)制退出。在運(yùn)行多少秒無(wú)果后(比如異步查詢(xún)sql server,一直連接不上),應(yīng)該有強(qiáng)制退出的方法,這個(gè)特性蠻重要的。所以只能自己手動(dòng)處理

2、缺少自定義消息躉,在使用類(lèi)時(shí)(特別是帶窗口的),自己構(gòu)建自定義消息躉,會(huì)讓程序控制粒度更細(xì),穩(wěn)定性更好。

3、錯(cuò)誤處理:錯(cuò)誤處理做得不夠好。一般子線(xiàn)程運(yùn)行出現(xiàn)錯(cuò)誤,應(yīng)該將錯(cuò)誤回調(diào)給主線(xiàn)程,進(jìn)行處理。所以你也只能手動(dòng)進(jìn)行錯(cuò)誤回調(diào)。

注:msgbox函數(shù)是一個(gè)阻塞式函數(shù),在子線(xiàn)程中調(diào)用msgbox函數(shù),會(huì)引起包括主線(xiàn)程及所有其它子線(xiàn)程都掛起。
3#
 樓主| 發(fā)表于 2016-12-19 12:57:25 | 只看該作者
本帖最后由 ganlinlao 于 2016-12-19 22:37 編輯

多線(xiàn)程使用場(chǎng)景1:使用多個(gè)子線(xiàn)程運(yùn)行函數(shù)。
例子中的函數(shù)很簡(jiǎn)單,只是為了展示一下 子線(xiàn)程如何運(yùn)行一個(gè)函數(shù)。效果跟單線(xiàn)程沒(méi)啥區(qū)別。
因?yàn)橐粋(gè)子線(xiàn)程運(yùn)行一個(gè)函數(shù)或過(guò)程,無(wú)論在子線(xiàn)程中怎么循環(huán),最終獲得返回值,也只是跟主線(xiàn)程通訊一次。
所以安全性很高。要開(kāi)多少個(gè)子線(xiàn)程,完全由你決定。
當(dāng)然線(xiàn)程開(kāi)得越多,性能反而會(huì)下降。所以一般cpu是幾核,就開(kāi)幾個(gè)線(xiàn)程。其余的沒(méi)必要多開(kāi)。

附件:

本帖子中包含更多資源

您需要 登錄 才可以下載或查看,沒(méi)有帳號(hào)?注冊(cè)

x
4#
 樓主| 發(fā)表于 2016-12-19 12:57:37 | 只看該作者
本帖最后由 ganlinlao 于 2016-12-21 09:12 編輯

使用場(chǎng)景二:多個(gè)子線(xiàn)程與主線(xiàn)程頻繁交互通信。
比如說(shuō)顯示進(jìn)度條,就是一個(gè)比較常見(jiàn)情況。
在這種情況下,VBMThread的不穩(wěn)定性就很明顯。相比之下,vbrichclient因?yàn)檎{(diào)用的是vb6編譯過(guò)的dll函數(shù),穩(wěn)定性非常好。
使用vbmthread,盡量只使用一個(gè)子線(xiàn)程跟主線(xiàn)程頻繁通信。vbrichlient則沒(méi)有這個(gè)限制。


這個(gè)例子只是簡(jiǎn)單地模擬一下進(jìn)度條。



本帖子中包含更多資源

您需要 登錄 才可以下載或查看,沒(méi)有帳號(hào)?注冊(cè)

x
5#
 樓主| 發(fā)表于 2016-12-19 12:57:51 | 只看該作者
本帖最后由 ganlinlao 于 2016-12-21 21:42 編輯

使用場(chǎng)景三:異步查詢(xún)與Recordset

        多線(xiàn)程一般涉及到網(wǎng)絡(luò)方面往往就能顯示出巨大的優(yōu)勢(shì),特別是異步。想想網(wǎng)頁(yè)的ajax技術(shù)幾乎是唯一的標(biāo)配,就能知道異步對(duì)于網(wǎng)絡(luò)的重要性了。
而在access中,如果你的數(shù)據(jù)庫(kù)是在本地的Pc上,則多線(xiàn)程意義不大,特別是access的綁定技術(shù)非常的強(qiáng)悍,使用多線(xiàn)程反而增加無(wú)謂的不安全和性能下降。但如果是access+ SQL server這種網(wǎng)絡(luò)型的,異步多線(xiàn)程,在某些方面則有明顯的優(yōu)勢(shì)。
       應(yīng)該說(shuō)異步,能讓界面不阻塞,從而使操作更流暢,雖然通過(guò)單線(xiàn)程的事件也能實(shí)現(xiàn)同樣的功能,但使用異步還是更好一些。
      比如一些特殊場(chǎng)合:
      登陸界面的驗(yàn)證、注冊(cè)時(shí)的用戶(hù)名即時(shí)驗(yàn)證,下拉列表的逐字提示……使用異步效果會(huì)更好一些。
     
      在Access中不建議對(duì)主要的Recordset使用多線(xiàn)程,因?yàn)樯婕暗酱绑w的綁定,使用多線(xiàn)程反而不太好。而且根據(jù)以前很多使用c++的人多線(xiàn)程調(diào)用Recordset,都建議一個(gè)線(xiàn)程開(kāi)一個(gè)connection,如果共用一個(gè)connection,會(huì)不定地出現(xiàn)問(wèn)題。而且據(jù)說(shuō)Recordset在多線(xiàn)程場(chǎng)合下有bug。
我在這里指的是多線(xiàn)程調(diào)用Recordset,并要最后進(jìn)行數(shù)據(jù)更新。如果只是查詢(xún),那是另外一回事了。
    VBMthread可以在線(xiàn)程間傳遞Recordset。但VBrichclient似乎傳遞不了,不知道VBrichclient的線(xiàn)程庫(kù)是不是沒(méi)有實(shí)現(xiàn)Istream接口,至少到目前為止,我還沒(méi)有找到傳遞Recordset的方法。如果你找到方法了,希望告知一下。
      

6#
 樓主| 發(fā)表于 2016-12-19 13:13:38 | 只看該作者
占位
回復(fù)

使用道具 舉報(bào)

點(diǎn)擊這里給我發(fā)消息

7#
發(fā)表于 2016-12-19 13:20:16 來(lái)自手機(jī) | 只看該作者
Access的黑科技來(lái)了
來(lái)自: 微社區(qū)

點(diǎn)擊這里給我發(fā)消息

8#
發(fā)表于 2016-12-19 16:48:13 | 只看該作者
大神來(lái)了,處理數(shù)據(jù)的能力提高了很多,速度快的不要不要的。

點(diǎn)擊這里給我發(fā)消息

9#
發(fā)表于 2016-12-23 13:54:59 | 只看該作者
這個(gè)牛啊
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則

QQ|站長(zhǎng)郵箱|小黑屋|手機(jī)版|Office中國(guó)/Access中國(guó) ( 粵ICP備10043721號(hào)-1 )  

GMT+8, 2024-10-23 08:29 , Processed in 0.156212 second(s), 33 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回復(fù) 返回頂部 返回列表