《elisp 每日一练》——异步进程基础与生命周期管理

《elisp 每日一练》——异步进程基础与生命周期管理

Masimaro
2026-10-23 / 0 评论 / 1 阅读 / 正在检测是否收录...

在上一节,我们讨论了Emacs中同步方式调用进程,它比较简单,适合那些能快速返回的命令行程序,对于一些耗时操作,如果下一步确实依赖耗时操作的返回,那么等待可能是必须的。但是某些时候,如果因为一些功能增强的后台应用导致界面卡顿,例如拼写检查、lsp 代码分析 如果耗时太久反而影响用户编写代码的体验。对于这种我们可以考虑将它们放到后台作为异步进程

背景知识

Emacs 提供了两个启动异步进程的函数: start-process 和 make-process。前者是早期Emacs提供的方式,它比较简单和传统,函数签名如下:

(start-process NAME BUFFER PROGRAM &rest PROGRAM-ARGS)

它的功能比较简单,它启动一个异步进程并将进程输出放到指定的buffer中,这种情况下我们无法对进程生命周期和输出进行精准的控制,如果需要更精确的控制,更推荐使用 make-process

(make-process &rest ARGS)

它的签名看着简单,似乎只有一个 ARGS 作为可选,但是实际上可选的参数十分丰富。我们传入一些关键字参数来精确的控制异步进程。最关键的几个参数如下:

  • :name 进程名称,Emacs必要时会自动修改这个名称,确保唯一;请注意这个名称是我们为这个进程对象取得名字,并不是可执行程序得名称
  • :command 这个是进程真实执行得命令行,可以是一个个命令行参数组成得字符串列表
  • :buffer 指定进程输出关联的缓冲区,可以是缓冲区名称或者对象
  • :filter 输出过滤函数,如果指定了函数,有输出时会调用该函数进行过滤,并将过滤后的内容输出到指定buffer中
  • :sentinel 进程状态变更的回调函数
  • :stderr 标准错误输出的buffer

调用这些函数之后,会返回一个进程对象。我们后面可以以对象作为参数,调用下面的函数来控制它的生命周期:

  • process-live-p: 检查进程当前是否还在活跃运行
  • process-status: 获取进程状态,例如 'run、'exit、'signal
  • interrupt-process: 向进程发送中断信号
  • kill-process: 强行终止进程

练习

我们实现一个调用cmake编译的小插件。假设我有一个项目在Windows上,我执行编译的命令如下:

cmake . -B build -G"Visual Studio 18 2026" -T "v143" -A x64 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL

cmake --build build

将编译的正常输出放到名为 *compile-build* 的buffer中,将错误和警告信息放到 *compile-error* 我们可以在该buffer中开启 compilation-minor-mode,这样我们就可以根据行号快速打开报错的位置

(defun my-cmake-msvc-build (source-dir)
  "打开当前buffer所在目录,执行cmake编译"
  (interactive "D选择项目根目录:")
  (let* ((default-directory (file-name-as-directory source-dir))
    (build-dir "build")
    (out-buf (get-buffer-create "*compile-build*"))
    (err-buf (get-buffer-create "*compile-error*"))
    (config-proc nil) ;; 构建进程
    (build-proc nil)) ;; 编译进程
    (with-current-buffer err-buf ;;启用 compilation-minor-mode 支持从错误信息跳转到具体位置
      (erase-buffer)
      (compilation-minor-mode t))

    (with-current-buffer out-buf
      (erase-buffer))

    (message "开始cmake 配置")
    (setq config-proc
      (make-process
        :name "cmake-config"
        :command (list "cmake" "." "-B" build-dir
          "-G" "Visual Studio 18 2026"
          "-T" "v143"
          "-A" "x64"
          "-DCMAKE_BUILD_TYPE=Debug"
          "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL")
        :buffer out-buf
    :stderr err-buf
    :noquery t
    :sentinel
    ;; 在构建成功后执行编译
    (lambda (p event)
      (when (memq (process-status p) '(exit signal))
        (if (zerop (process-exit-status p))
          (progn
         (message "构建成功,开始编译...")
         (setq build-proc
           (make-process
             :name "cmake-build"
             :command (list "cmake" "--build" build-dir)
             :buffer out-buf
             :stderr err-buf
             :noquery t
             :sentinel
             ;; 通知结果
             (lambda (p1 event1)
               (when (memq (process-status p1) '(exit signal))
              (if (zerop (process-exit-status p1))
                  (message "构建成功")
                (message "构建失败,查看*compile-error*")
                (display-buffer err-buf))))))))))))
    config-proc))

这个例子,我们演示了 make-process 的基本用法,并且给出了状态变更的回调函数的使用。在Windows上实测时因为msvc将编译时的错误信息和正常输出都作为标准输出了,所以读者可能会发现报错出现在 *compile-build* 缓冲。为了适配Windows,读者可以尝试在 *compile-build* 也加上 compilation-minor-mode

总结

本节我们介绍了异步方式调用的进程的方法,本节的相关函数如下:

  • make-process
  • process-live-p
  • process-status
  • interrupt-process
  • kill-process
0

评论 (0)

取消