- async 20170804.2158(in MELPA)
- Asynchronous processing in Emacs
概要
async.el はEmacs Lispで 並列処理 を行うライブラリです。
作者はEmacsの世界的権威 John Wiegley 氏。
eshellやuse-package.el 、bind-key.el など
多くのEmacs Lispをリリースしています。
Emacs Lispでは並列処理が弱点と言われていますし、
できないと思っている人もいるようですが、
実はある方法を使えば可能なのです…
確かにEmacs Lispという言語自体では
並列処理の機能はないのですが、
プロセス を使うという抜け道があります。
M-x shellでシェルを動かしながら
Emacsを動作させているのと同じです。
async.elは新しいEmacsプロセスでS式を評価したり、
S式を返す外部プログラムを起動したりすることで
Emacs Lispで並列実行を行います。
deferred.el でも並列処理をしますが、
あれはタイマーによる疑似スレッドなので
残念ながらせっかくのマルチコアが生きません。
インストール
パッケージシステムを初めて使う人は
以下の設定を ~/.emacs.d/init.el の
先頭に加えてください。
(package-initialize) (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "http://melpa.org/packages/") ("org" . "http://orgmode.org/elpa/")))
初めてasyncを使う方は
以下のコマンドを実行します。
M-x package-install async
アップグレードする方は、
以下のコマンドでアップグレードしてください。
そのためにはpackage-utilsパッケージが必要です。
M-x package-install package-utils (初めてアップグレードする場合のみ) M-x package-utils-upgrade-by-name async
async-start
async-start 関数は新しいEmacsプロセスで評価するラムダ式と、
実行終了時に評価する関数の2つを取り、並列実行をします。
新しいEmacsプロセスは
emacs -Q -batch により
まっさらな環境で評価します。
そのため、現在の変数の値をそのまま引き継ぐことはできません。
そこで async-inject-variables で新しいEmacsに
持ち込む変数を正規表現で指定します。
返り値は、それらの変数を設定するsetqのS式です。
たとえば、a=3を持ち込む場合は
(async-inject-variables "^a$") ↓ (setq a (quote 3))
となります。
これを使う場合は バッククォート でラムダ式を作ります。
変数を参照するには レキシカルスコープ が必要なので
lexical-let や lexical-binding を使う必要があります。
;;; -*- lexical-binding: t -*- (require 'async) ;;; tmを後のlambdaで参照するためlexical-bindingが必要 (let ((tm (float-time))) (async-start ;; What to do in the child process (lambda () (message "This is a test") (sleep-for 3) 222) ;; What to do when it finishes (lambda (result) (message "Async process done, result should be 222: %s (%s sec)" result (- (float-time) tm))))) ;;; 誤例: nil 新プロセスでの (buffer-file-name) を評価してしまう (async-start (lambda () (buffer-file-name)) (lambda (result) (message "buffer-file-name: %s" result))) ;;; バッククォートにより結果を渡すことでカレントバッファのbuffer-file-nameが返る (async-start `(lambda () ,(buffer-file-name)) (lambda (result) (message "buffer-file-name: %s" result))) ;;; 変数custom-fileをプロセスに渡す→現在のcustom-fileが返る (async-start `(lambda () ,(async-inject-variables "^custom-file$")) (lambda (result) (message "buffer-file-name: %s" custom-file)))
async-start-process
async-start-process 関数は、任意のプロセスを立ち上げ、
その結果を コールバック関数 に渡します。
とくにプロセス名(第1引数)がemacsのとき、
コールバックに渡される引数は
そのプロセスの出力結果のS式です。
これはなかなか興味深い機能で、S式を出力する外部プログラムを作成すれば、
Emacs Lispで書くと遅い処理でもEmacsが止まることなく処理してくれるでしょう。
たとえばアイドル時にカレントバッファのソースコードを解析して、
その結果を変数に保持するようなこととかです。
夢を見させてくれる機能ですが、やはりネックは
現在の状態をいかにプロセスに渡すかでしょう。
この例では簡単のためににechoでS式を出力させているだけです。
;;; 単にプロセスが返る (async-start-process "hoge" "echo" (lambda (x) (message "ret:%S" x)) "(+ 1 2)") ;;; プロセス名にemacsを指定すると、出力をS式とみなしてくれる→(+ 1 2)が返る (async-start-process "emacs" "echo" (lambda (x) (message "ret:%S" x)) "(+ 1 2)") ;;; evalすると3が返る (async-start-process "emacs" "echo" (lambda (x) (message "ret:%S" (eval x))) "(+ 1 2)")
本サイト内の関連パッケージ
本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。