前言
春節(jié)剛過待讳,我在這里給大家拜年了,祝大家在新的一年里學(xué)習(xí)進步仰剿,事業(yè)有成创淡!在剛剛過去的2016年里,Emacs 25版本發(fā)布了南吮。按照每年發(fā)布一個大版本的慣例琳彩,2017年,期待Emacs 26版本早點發(fā)布部凑。那么露乏,你最期待的Emacs 26有哪些特性呢?不瞞你說涂邀,小編最期待的特性是多線程瘟仿。年初的時候,多線程的特性已經(jīng)合入到Master分支比勉。不過劳较,之前多線程在Mac下有一個致命的問題:一旦調(diào)用 make-thread 函數(shù)就會導(dǎo)致CPU飆升至100%,Emacs卡死浩聋。春節(jié)過后兴想,小編又試了下最新的Emacs 26的包,發(fā)現(xiàn)赡勘,那個致命的BUG得到了修復(fù),在Mac下能正常跑多線程代碼捞镰。那么闸与,今天我們簡單聊聊Emacs 26的多線程。
多線程與異步
在Emacs沒提供多線程的特性的時候岸售,如果開發(fā)者想使用異步的包践樱,那可以采用async(鏈接為:https://github.com/jwiegley/emacs-async)。這個異步包采用的是創(chuàng)建一個獨立的子進程進行后臺操作凸丸。這種異步方式最大的缺點是新的子進程遠程與Emacs主進程不共享上下文(如不共享變量),它完全是獨立的拷邢。相反,多線程的方式有一個優(yōu)點就是屎慢,多個線程間共享同一個上下文瞭稼,可修改同一個變量。但有的人認為創(chuàng)建子進程這種異步才是"正常"的異步方式腻惠。因為环肘,如果采用多線程的方式,考慮到會產(chǎn)生多個線程同時修改同個變量(或者Buffer)可能導(dǎo)致Emacs詭異行為集灌。
安裝Emacs 26開發(fā)版
要想在Emacs 26發(fā)布之前使用多線程的功能悔雹,必需升級Emacs到最新的開發(fā)版本。Mac環(huán)境下,下載Daily Build的開發(fā)版本腌零,地址為https://emacsformacosx.com/builds 梯找。我下載的是2017-02-05這天的Daily Build開發(fā)版 (版本號:26.0.50.1)。如果是Linux環(huán)境益涧,可以自己下載源代碼進行源碼編譯安裝锈锤。源碼下載
git clone --depth 1 git://git.sv.gnu.org/emacs.git
嘗鮮Emacs多線程
安裝好最新版本的Emacs后,下面我們嘗試下多線程饰躲。創(chuàng)建新的線程的函數(shù)是make-thread 牙咏,這個函數(shù)的定義如下:
(make-thread FUNCTION &optional NAME)
它的作用是創(chuàng)建一個新的線程,然后在這個線程中執(zhí)行FUNCTION這個函數(shù)嘹裂,當(dāng)函數(shù)執(zhí)行完成后退出時妄壶,這個線程就結(jié)束了。 NAME這個字符串參數(shù)是可選的寄狼,作用是對這個線程進行命名丁寄。
多線程的異步
下面,我們定義一個宏泊愧,將函數(shù)的執(zhí)行在異步線程里:
(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))))))))
這個宏的作用是將函數(shù) 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)
然后我們執(zhí)行 M-x bg-threadaction 會在*Messages*這個Buffer打印出執(zhí)行日志!從日志中我們看出,在執(zhí)行異步線程時删咱,可以同步操作Emacs屑埋。
如果采用async怎么寫
上面的例子,如果采用async進行異步操作痰滋,寫法如下:
(defun async-threadaction ()
"Emacs async example runner."
(interactive)
(async-start
;; 異步執(zhí)行更新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
小結(jié)
以上對Emacs 26多線程簡單描述敲街,我們發(fā)現(xiàn)多線程對Emacs帶來了一種全新的編輯體驗团搞。我們在操作Emacs也不再會受后臺任務(wù)的影響。這將是Emacs 26最期待的功能多艇。想嘗鮮的同學(xué)可以下載Daily Build開發(fā)版逻恐。想要Emacs 26 Release版本的同學(xué),那只能希望Emacs 26早日發(fā)布了峻黍!