Emacs 26 的多线程探索

发布于 2023-04-25 13:27:24 字数 4106 浏览 76 评论 0

多线程的特性已经合入到 Master 分支。不过,之前多线程在 Mac 下有一个致命的问题:一旦调用 make-thread 函数就会导致 CPU 飙升至 100%,Emacs 卡死。
春节过后,小编又试了下最新的 Emacs 26 的包,发现,那个致命的 BUG 得到了修复,在 Mac 下能正常跑多线程代码。那么,今天我们简单聊聊 Emacs 26 的多线程。

多线程与异步

在 Emacs 没提供多线程的特性的时候,如果开发者想使用异步的包,那可以采用 async(链接为:https://github.com/jwiegley/emacs-async )。这个异步包采用的是创建一个独立的子进程进行后台操作。这种异步方式最大的缺点是新的子进程远程与 Emacs 主进程不共享 上下文(如不共享变量),它完全是独立的。

相反,多线程的方式有一个优点就是,多个线程间共享同一个上下文,可修改同一个变量。但有的人认为创建子进程这种异步才是 正常 的异步方式。因为,如果采用多线程的方式,考虑到会产生多个线程同时修改同个变量(或者 Buffer)可能导致 Emacs 诡异行为。

安装 Emacs 26 开发版

要想在 Emacs 26 发布之前使用多线程的功能,必需升级 Emacs 到最新的开发版本。Mac环境下,下载 Daily Build 的开发版本,地址为:https://emacsformacosx.com/builds 。我下载的是 2017-02-05 这天的 Daily Build 开发版(版本号:26.0.50.1)。

如果是 Linux 环境,可以自己下载源代码进行源码编译安装。(源码下载: git clone --depth 1 git://git.sv.gnu.org/emacs.git

尝鲜 Emacs 多线程

安装好最新版本的 Emacs 后,下面我们尝试下多线程。创建新的线程的函数是 make-thread ,这个函数的定义如下:

(make-thread FUNCTION &optional NAME)

它的作用是创建一个新的线程,然后在这个线程中执行 FUNCTION 这个函数,当函数执行完成后退出时,这个线程就结束了。 NAME 这个字符串参数是可选的,作用是对这个线程进行命名。

多线程的异步

下面,我们定义一个宏,将函数的执行在异步线程里:

(defmacro define-background-function-wrapper (bg-function fn)
  (let ((is-loading-sym (intern (concat "*" (symbol-name bg-function) "-is-loading*"))))
    `(progn
       (defvar ,is-loading-sym nil)
       (defun ,bg-function ()
         (interactive)
         (when ,is-loading-sym
           (message ,(concat (symbol-name fn) " is already loading")))
         (setq ,is-loading-sym t)
         (make-thread (lambda ()
                        (unwind-protect
                            (,fn)
                          (setq ,is-loading-sym nil))))))))

这个宏的作用是将函数 fn 运行在异步线程里。下面是一个使用的例子

(defun aborn/log-format (origin)
  "Format `ORIGIN' log with timestamp."
  (concat (format-time-string "[%Y-%m-%d %H:%M:%S] " (current-time))
          origin))

(defun threadaction ()
  "Emacs multi-thread example runner."
  (message (aborn/log-format "begin running..."))
  (sleep-for 1)
  (message (aborn/log-format "running at point 1"))
  (sleep-for 5)
  (message (aborn/log-format "running at point 6"))
  (sleep-for 10)
  (message (aborn/log-format "running at point 16"))
  (sleep-for 15)
  (message (aborn/log-format "finished bg-runner.")))

(define-background-function-wrapper bg-threadaction threadaction)

然后我们执行 M-x bg-threadaction 会在 Messages 这个 Buffer 打印出执行日志,从日志中我们看出,在执行异步线程时,可以同步操作 Emacs。

如果采用 async 怎么写

上面的例子,如果采用 async 进行异步操作,写法如下:

(defun async-threadaction ()
  "Emacs async example runner."
  (interactive)
  (async-start
   ;; 异步执行更新code操作
   `(lambda ()
      ,(async-inject-variables "\\`load-path\\'")
      (require 'aborn-log)
      (message (aborn/log-format "begin running..."))
      (sleep-for 1)
      (message (aborn/log-format "running at point 1"))
      (sleep-for 5)
      (message (aborn/log-format "running at point 6"))
      (sleep-for 10)
      (message (aborn/log-format "running at point 16"))
      (sleep-for 15)
      (message (aborn/log-format "finished bg-runner."))
      )
   (lambda (result)
     (message "finished"))))

注意这里添加了这两句:

,(async-inject-variables "\\`load-path\\'")
(require 'aborn-log)

目地是将主进程的 aborn-log 的上下文引入到子进程中去,如果将这两句去掉,就会报这样的错误:

error in process sentinel: async-handle-result: Symbol's function definition is void: aborn/log-format

小结

以上对 Emacs 26 多线程简单描述,我们发现多线程对 Emacs 带来了一种全新的编辑体验。我们在操作 Emacs 也不再会受后台任务的影响。这将是 Emacs 26 最期待的功能。想尝鲜的同学可以下载 Daily Build 开发版。

想要 Emacs 26 Release 版本的同学,那只能希望 Emacs 26 早日发布了!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
28 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文