Emacs 26 的多线程探索
多线程的特性已经合入到 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 技术交流群。

上一篇: 如何玩转 tramp?
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论