我最早的时候接触的编程语言是VB, VB中有一个doEvents函数,当一个循环时间很长,很耗费资源时,可以释放部分资源,让CPU去处理其他事情,同时也使用户界面不会被锁住,而继续响应鼠标和键盘的操作。
VB中的用法就像这样:
http://www.d-programming-language-china.org/java-vb-doevents/for i=1 to 10000000
print i^2
doEVents S
next i
我现在用D语言编程,D语言界面库最重要的是DWT, 也就是D语言版的JAVA SWT. 但是JAVA没有doEvents函数。要进行类似的操作时怎么办?
D语言有一个DFL界面库,其中有一个doEvents函数,用法和功能与VB中一样。但DFL是个人开发的,功能有限,也不知道这个项目什么时候会挂掉。因此,我是尽量不去用DFL中的函数。但在没有替代办法前,在D语言库DWin http://dwin.d-programming-language-china.org/库包装引用了DFL的doEvents函数。
我刚升级了所用的D语言编译器dmd到最新版1.041,并把D语言最重要的基础库Tango更新到svn revision 4439,把dwt2更新到最新,这样DFL就不能编译通过,不能与其他库一起使用。而我的程序中有的地方用到了doEvents这个函数。虽然我可以自己修改DFL使其通过编译,但一直这样做太烦琐了。所以,我需要自己实现一个 doEvents函数。
上网查找有没有 JAVA版的VB doEvents函数,在国内和国外都没有找到解决办法。可能这个问题太简单了吧,有人提问,但找不到代码实现。
查了DWT的Display模块,和doEvents相应的方法有:
readAndDispatch()
sleep()
wake()
SWT程序一般结尾代码一般是这样的:
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
在这个循环里,首先确保用户没有关闭主窗口。因为如果窗口如果是开着的,就可以检查事件消息队列是否有窗口系统或者程序其他部分可能产生的消息。如果没有消息的话,程序就休眠,把控制权交给操作系统,等待下一个事件得到达。当下一个事件到达后,重复循环,确保事件没有关闭主要窗口。
这个循环的sleep()方法就相应于VB的doEvents, 我们自己的doEvents就要围绕display.sleep()来写。
如果没有事件来唤醒,sleep就会一直休眠下去,而我们在doEvents时往往是要求定时的。所以我们还要用到display.wake()函数来唤醒休眠。
swt doEvents 1
在doEvents函数的开始,我们要用display.timerExec(…)来定时唤醒sleep, 然后在while循环中sleep直到被唤醒。代码如下,这里的代码是基于D语言和Windows环境的,在JAVA SWT中原理一样:
// by D语言中国 http://www.d-programming-language-china.org/
void doEvents(int msDelay = 3000) {
void wake() { display.wake(); }
// //指定时间到后,会在readAndDispatch中被检测到而得到调用
display.timerExec(msDelay, dgRunnable(&wake));
uint begin = GetTickCount(); //windows api 函数
//如果指定时间不到就一直休眠
while( (GetTickCount() - begin) < msDelay ) {
if(!shell.isDisposed())
if (!display.readAndDispatch())
display.sleep();
}
}
swt doEvents 2
用上面的版本,如果程序在执行时发生Access Violatin错误,可以用下面的简化版,去掉display.sleep:
// by D语言中国 http://www.d-programming-language-china.org/
void doEvents(int msDelay = 3000) {
uint begin = GetTickCount(); //windows api 函数
while((!shell.isDisposed() || (GetTickCount() - begin) < msDelay ) {
display.readAndDispatch();
}
}
swt doEvents 3
有时,在程序中上面两个版本的doEvents都不能用,可以试用下面这个:
void doEvents(int msDelay = 3000) {
for(int i; i < msDelay && !shell.isDisposed(); i++)
display.readAndDispatch();
}
Windows下的VB doEvents实现
上面的swt doEvents例子,其实和VB中的doEvents是不一样的。要真正实现VB doEvents类似的效果,还需要直接调用Windows API, 下面是D语言库DWin http://dwin.d-programming-language-china.org/库中的doEvents例子:
bool doEvents() {
MSG msg;
while(PeekMessageW(&msg, HWND.init, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
if(msg.message == WM_QUIT)
return false;
}
return true;
}
}
bool doEvents(Display display, Shell shell, uint msDelay) {
uint begin = GetTickCount();
while( (GetTickCount() - begin) < msDelay ) {
if(shell.isDisposed() || !doEvents())
return false;
}
return true;
}
}
复杂的程序,比较长时间的操作,我们一般把工作放入另外工作线程中执行,而不是用doEvents的方法。把工作线程的优先级设置得低一些,在工作时界面会有反应,但工作时间就增加了。
http://www.d-programming-language-china.org/java-vb-doevents/JAVA SWT版的VB doEvents延时
