You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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空闲反应器上:

  1. 启动动画时只做状态初始化、注册空闲回调,立刻把执行权交还给主程序
  2. 主程序运行过程中,只要AutoCAD出现执行间隙(比如执行命令、等待IO、延时等待时),就会自动触发回调刷新一帧动画
  3. 主程序运行结束后,手动移除反应器、重置状态、输出结束提示即可

改造后可直接使用的代码

使用前需要先在代码开头加(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:00:49