- UID
- 77
- 精华
- 积分
- 9576
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
如何在VB6里实现稳定多线程,一直是网上争论不休的话题。网上基本上有两派,第一派是通过修补VB程序的机器码和调用特定函数来实现多线程。但是经我测试,无论哪个版本、无论作者号称多么稳定,其实都是不稳定的。我前段时间做了个《坦克大战》,使用单线程时非常稳定,而使用网上大神们的“魔改多线程”时,玩不到20分钟程序就会卡死或崩溃。而第二派,就是使用ActiveX-EXE实现多线程。网上对ActiveX-EXE实现多线程的稳定性没有质疑,但是在其它方面有诸多指责,比如说在ActiveX-EXE程序里,句柄不能共享,全局变量不能用等等。其实这些指责里,一半是谣言,一半是误解(因为没有搞清楚ActiveX-EXE的多线程特性造成的)。
在讨论多线程之前,先讨论一下,VB里什么物件是全局的?所有人都会说:窗体对象是全局的,模块是全局的,全局变量和常量也是全局的。但是“全局物件”又有两层含义,第一层是,写在任何地方的代码都可以直接访问它们,第二层是,它们没有“分身”。这时有人要说了,你唠唠叨叨说这些废话跟多线程有什么关系呢?我必须指出的是,VB里没有任何“自带物件”在进程内是全局的,它们仅是在线程内全局。一旦新建了线程,并且当新线程访问这些全局物件时,所有的物件都会给访问它的线程创造一个“分身”。即使是模块里的全局函数也不例外。举一个例子:- #include <stdio.h>
- #include <stdlib.h>
- #include <windows.h>
- long StaticTest()
- {
- static long v = 0;
- return ++v;
- }
- DWORD WINAPI ThreadProc(LPVOID lpParameter)
- {
- printf("[%ld]%ld\n",GetCurrentThreadId(),StaticTest());
- return 0;
- }
- int _cdecl main(int argc, char **argv)
- {
- CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
- Sleep(500);
- CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
- Sleep(500);
- CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
- system("pause");
- return 0;
- }
复制代码 这段C程序的三次输出,应该分别是1、2、3。但如果用VB的AvtiveX-EXE写同样的代码,它们的输出分别是1、1、1!因为正如我上面所说,虽然在代码里,StaticTest函数是全局的,但是ActiveX-EXE的每一个线程里,它们都有自己独立的StaticTest!看懂了上文,你就会明白为什么会有“ActiveX-EXE里连句柄都不能共享”的谣言:因为即使是全局变量,线程A根本就无法直接访问到线程B的全局变量。但是这个句柄只要产生,它在进程里就是有效的。你只要想办法把线程A产生的句柄值让线程B得知即可。这里面的方法太多了,小白用SaveSetting/GetSetting、驱动老哥用DOS头的废弃空间、学院派用文件映射,不管什么方法,能达成目标就行。此外,某个线程要访问其它线程的变量和窗口/类是可行的,但必须用指针,指针的值也可以同样用上述方法传递。
接下来解释在ActiveX-EXE里玩多线程的具体步骤。首先你要新建一个模块,写一个Sub Main。任何ActiveX-EXE的代码都从Sub Main开始执行,这点对于习惯了C语言的人都很好理解。在Sub Main里你要创建一个主窗口,让Sub Main的代码尽快结束。这一步是必须的,因为创建多个线程的代码在Sub Main里无效,因此接下来的代码只能写在窗口里。需要注意的是,由于任何线程在启动的时候都会执行一次Sub Main,因此你要在Sub Main里设置一个“事件”,防止你的初始化代码被重复执行。接下来就是关于多线程代码的核心步骤了:新建一个类模块,但这个类模块只是一个“药引”,它唯一的作用是在Class_Initialize里Load一个窗口(这个窗口被称为“多线程代码窗口”)。在“多线程代码窗口”里,你要放置一个Interval为1的Timer1,在Timer1_Timer里,先写一句Timer1.Enabled = False。在这句之后,才写你自己的代码。当线程执行完毕要退出时,简单一句Unload Me即可。- Private Sub Timer1_Timer()
- Timer1.Enabled = False
- MsgBox CStr(App.ThreadID), , "tid"
- Unload Me
- End Sub
复制代码
当创建新线程的时候,只需要三行代码:- Dim o As Object
- Set o = CreateObject("app.class")
- Set o = Nothing
- '//其实你可以把这三行理解为:
- 'HANDLE h;
- 'h = CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
- 'CloseHandle(h);
复制代码
最后稍微解释一下怎么让ActiveX-EXE正确运行,需要修改两个设置:
需要注意的是,多线程效果只有在编译为EXE后才有效:
完整DEMO代码: |
|