AutoLisp如何不等待返回同时运行Whirl函数与主代码
问题
我编写了一个Whirl等待动画提示函数,希望它能与主程序代码并行运行,请问如何调用Whirl函数,才能无需等待其返回执行结果,就直接继续运行后续主代码逻辑?
原有实现代码
;; 全局变量 (setq *bWhirl* nil) ;; 测试函数 (defun C:fcnTest01() ;; 期望不等待返回就继续执行 (Whirl "Working..." "Done") ;; 主业务代码 (princ "\nRuns program.\n") (command "DELAY" "5000") ;; 收尾逻辑 (setq *bWhirl* nil) (terpri)(princ) );defun C:fcnTest01 ;; Whirl动画函数 (defun Whirl (sFrontText sEndText / rWhirl) ;; 参数校验 (if (/= (type sFrontText) 'STR)(progn (princ "\n输入变量不是字符串类型。") (exit) ));if<-progn ;; 初始化 (setq rCmdEcho (getvar "CMDECHO"))(setvar "CMDECHO" 0) (setq *bWhirl* T) (terpri); 避免清空之前的命令行输出 ;; 循环等待标记位变化 (while *bWhirl* (if rWhirl (setq rWhirl (1+ rWhirl)) (setq rWhirl 1) );if (if (> rWhirl 4)(setq rWhirl 1)) (princ (strcat "\r" sFrontText (cond ((= rWhirl 1)"-") ((= rWhirl 2) "\\") ((= rWhirl 3) "|") ((= rWhirl 4) "/") )));princ <- strcat <- cond (command "DELAY" "500") );while ;; 收尾输出 (if (= (type sEndText) 'STR) (princ (strcat "\r" sFrontText sEndText)) (princ (strcat "\r" sFrontText)) );if (setvar "CMDECHO" rCmdEcho) (princ) );defun Whirl
注:该
Whirl函数修改自公开的AutoLISP绘图工具代码。
解答
AutoLISP是运行在AutoCAD主进程下的单线程解释型语言,你当前写的Whirl函数用了阻塞式while死循环,一旦调用就会一直占住执行权,直到*bWhirl*被置为nil才会退出,直接调用原函数不可能实现“不等待返回就执行后续代码”的效果,必须改造函数的运行逻辑,利用AutoCAD提供的空闲事件机制实现非阻塞的动画效果。
改造思路
把原来阻塞式的循环刷新逻辑拆出来,绑定到AutoCAD的LISP空闲反应器上:
- 启动动画时只做状态初始化、注册空闲回调,立刻把执行权交还给主程序
- 主程序运行过程中,只要AutoCAD出现执行间隙(比如执行命令、等待IO、延时等待时),就会自动触发回调刷新一帧动画
- 主程序运行结束后,手动移除反应器、重置状态、输出结束提示即可
改造后可直接使用的代码
使用前需要先在代码开头加(vl-load-com)加载ActiveX支持库,否则反应器函数无法运行:
(vl-load-com) ;; 全局状态变量 (setq *bWhirl* nil *whirl-reactor* nil *whirl-state* 0 *whirl-front-txt* "" *whirl-end-txt* "" *whirl-old-cmdecho* 0 ) ;; 动画单帧刷新回调 (defun whirl-refresh-frame () (if *bWhirl* (progn (setq *whirl-state* (1+ *whirl-state*)) (if (> *whirl-state* 4) (setq *whirl-state* 1)) (princ (strcat "\r" *whirl-front-txt* (cond ((= *whirl-state* 1) "-") ((= *whirl-state* 2) "\\") ((= *whirl-state* 3) "|") ((= *whirl-state* 4) "/") ) ) ) ) ) (princ) ) ;; 非阻塞启动等待动画 (defun Whirl (sFrontText sEndText / ) (if (/= (type sFrontText) 'STR) (progn (princ "\n错误:输入提示文本必须为字符串类型。") (exit) ) ) ;; 保存环境、初始化状态 (setq *whirl-old-cmdecho* (getvar "CMDECHO")) (setvar "CMDECHO" 0) (setq *whirl-front-txt* sFrontText *whirl-end-txt* sEndText *whirl-state* 0 *bWhirl* T ) (terpri) ;; 先清掉之前可能残留的反应器 (if *whirl-reactor* (vlr-remove *whirl-reactor*)) ;; 注册空闲反应器,执行间隙自动刷新动画 (setq *whirl-reactor* (vlax-lisp-reactor nil '((:vlr-lispIdle . whirl-refresh-frame))) ) (princ) ) ;; 停止等待动画 (defun Whirl-Stop (/ ) ;; 移除反应器 (if *whirl-reactor* (progn (vlr-remove *whirl-reactor*) (setq *whirl-reactor* nil)) ) ;; 输出结束文本 (if (= (type *whirl-end-txt*) 'STR) (princ (strcat "\r" *whirl-front-txt* *whirl-end-txt*)) (princ (strcat "\r" *whirl-front-txt*)) ) ;; 恢复环境、重置标记 (setq *bWhirl* nil) (setvar "CMDECHO" *whirl-old-cmdecho*) (princ) ) ;; 测试函数 (defun C:fcnTest01(/ ) ;; 启动动画,调用后立刻返回,不会阻塞后续代码 (Whirl "Working..." "Done") ;; 主程序逻辑直接执行 (princ "\nRuns program.\n") (command "DELAY" "5000") ;; 执行结束停止动画 (Whirl-Stop) (terpri)(princ) )
注意事项
- 这种实现是协作式的非阻塞效果,不是系统级多线程:如果主代码里有长时间占满CPU的密集计算逻辑、全程不释放执行权,动画会暂时卡顿,等计算让出执行权后才会继续刷新
- 一定要在主程序的所有退出分支(包括异常报错的出口)都调用
Whirl-Stop清理反应器,否则动画会一直残留运行
内容的提问来源于stack exchange,提问作者G Beck
相关产品推荐
相关产品推荐

