本帖最后由 ganlinlao 于 2016-12-19 13:26 编辑
兄弟,如果你和我一样是一个Access菜鸟,而且是属于无可救药的那种,那么在过去的很多年里,你一定听过很多很多的人不停地告诉你,VB6、VBA是无法使用多线程的,以至于你忍不住相信多线程是VBA不可逾越的障碍。忍不住相信多线程对于VBA来说只是传说中的诗和远方,远方遥远得一无所有。而我们这样的菜鸟只剩下眼前单线程的苟且,并把这种苟且当成一种美好、快乐和满足。
是的,他们说的没错,VBA有着一颗易碎而脆弱的玻璃心。
在过去的十多年里,无数的人都试图在vba里使用多线程,但几乎都倒在淋漓的血泊和无尽的让人抓狂的自虐中。
今天让我们揭开在VBA中使用多线程神秘的面纱,让这个沐浴着阳光梳着长发的美少女,能对你轻轻回眸一笑。
今天的cpu绝大多数是双核双线程、双核4线程、四核四线程、四核八线程、八核……,所以在VBA中使用多线程来提高运行效率显得很有必要。而且在一些场景下,你也很渴望能使用多线程来改善惨不忍睹的状况,比如在excel中的多个大循环计算会让excel表很卡,大批量复制文件,下载网络数据……都很容易造成access或excel界面出现“假死”。
在VBA中,其实可以使用不少的线程库,比如vbthreadfacory,vbrichclient5中线程库……事实上你能找到不少的vb线程库,前面两种线程库是我了解到的比较稳定而且功能比较完备的线程库。今天我在这里介绍是另一种更加简单易用的线程库,虽然它功能不那么完备,但却是简单明了,用法简单直接,能让我们更容易理解多线程的概念和入门使用。
关于进程、线程的概念,你可以百度或详见我的空间日志。
Access或excel的主线程:Access.exe是一个进程,access.exe会为每一个打开的access文件(指的是客户端)分配一个线程(这里我们暂且称此线程是主线程),这个Access文件中所有的Form,控件都在同一个线程中运行。注意是所有的Form及其下面的控件!同样的,excel.exe会为每一个打开的workbook分配一个线程,这个workbook中所有的表(包括每一个表及其下面的userform及控件)都运行在同一个线程中。了解这一点蛮重要的,因为这能让我们更注意保护子线程的安全,减少access或excel崩溃。
Access或excel的主线程消息处理:事件是一种消息,而消息是一种线性队列,所以所有子线程跟主线程的通讯,最终都会串口化成主线程的事件(消息),最终在主线程那里变成单线程的事件。主线程无法在同一时间点处理两个以上的事件,而这一点恰恰是access或excel在使用多线程上最容易引起崩溃的原因。而且要注意,access或excel的vba处理消息能力很差(因为我们不知道access处理完一个事件之后会不会在内部自动触发另一个我们不知道的事件,据我所知,excel内部事件远远比vba能用到的事件多得多),相比较而言,编译过的vb6程序在多线程方面则要稳定得多了。
如何避免让access在同一时间点处理两个事件,是我们使用vba多线程排在首位任务。任何引发access或excel在同一时间点处理两个以上事件,access或excel一定会崩溃。
在vba中使用多线程,以下是我个人简单的经验,以后你使用多了,也可以总结一下并写出来,让其他人少走一些弯路。
经验1:在任何调试多线程代码之前,一定要先保存。先保存后调试是铁律,否则很多意外,会让你欲哭无泪。
经验2:尽量减少子线程跟主线程的通讯次数。很多的时候,其实我们只需一个子线程跟主线程通讯而已(比如我们常常只需要显示一个进度条就够了)。如果进度条需要100次跟主线程通讯,那么想办法让它减少成只有10次,跟主线程的通讯次数越少,越能减少让access主线程同时处理两个消息的机会。
经验3:要及时主动销毁子线程占用的资源。不能指望access或excel自动帮你销毁。如果子线程占用资源没有销毁,会引发很多问题,甚至引起崩溃。所有一般对于子线程,我们尽量在需要时创建,用完立即销毁。
经验4:如果是控件类,尽量用vb6编译成dll,然后再在access中使用,我前面提到过了,vb6编译过的dll在多线程方面要稳定得多了。如果是写很多需要多线程调用的函数,那用vb6编译成dll,也会更稳妥一些。
经验5:当一个access的form不在激活状态,最好让它对应的子线程都处在休眠状态(挂起),当整个access客户端,不在激活状态,也最好让它对应的所有子线程都挂起。这样能大大减少让access同时处理两个消息的机会。这也同样适用excel。不要忘了,不管是access还是excel都是mdi多文档程序。 |