多谢这位同志
确实。真没有找到例子。
emacs目前实现的"多线程"应该叫"协程", 没法真正并发计算, 另外, 每个线程都可以操作gui, 导致混乱, 难以实用, 感觉设计得很失败.
这方面真应该参考浏览器, 毕竟浏览器的工作模型和emacs非常类似, 都是涉及高密度用户操作和界面更新. web worker的设计就相当不错.
最近好像看到一种思路, 就是fork一个emacs子进程, 实现类似web worker的模型, 把worker放在子进程运行, 运行结果返回回来, 关闭子进程. 这样好像也可以, 就是太耗费进程. async包好像也是这么工作的.
我是比较希望emacs能支持类似沙盒的东西, 在emacs进程里能创建多个沙盒, 每个沙盒相当于一个线程, 沙盒里面可以运行elisp, 但是不能访问emacs的全局变量(包括界面等等). 然后把worker放在沙盒里运行. 主线程的elisp可以管理和控制沙盒, 并跟沙盒里的worker交换数据.
我是比较希望emacs能支持类似沙盒的东西, 在emacs进程里能创建多个沙盒, 每个沙盒相当于一个线程, 沙盒里面可以运行elisp, 但是不能访问emacs的全局变量(包括界面等等). 然后把worker放在沙盒里运行. 主线程的elisp可以管理和控制沙盒, 并跟沙盒里的worker交换数据.
这个方法对现有的emacs生态应该是没有破坏, 就是不知道emacs内部好不好实现.
如果有沙盒,那运行js应该更好吧。那样的话,还得有一个对其他语言的接口,简单的rpc应该就行了。neovim就是这么做的。
我感觉多线程的支持可以逐步迁移。先实现一套新的emacs操作api,保证跟老的api优好合作,并保证没有并发问题。然后新的插件就可以使用新的api写了,老的插件也可以慢慢迁移。更进一步可以添加一个关键字可以把老的emacs api编译成新的api(例如python 里面的async await?/). 之后迁移的动作就比较小了。
不需要这么复杂,可以直接暴力添加并行处理,不能接受这个的现有 lisp 代码不从非主要处理单位调用就行了。主要是 C 端需要做到安全,而已有的协作性线程已经解决大部分问题 (比如说 specpdl 等),只留下 GC 和 wait_reading_process_output 这几个难题。
(let ((case-fold-search nil))
(search-forward "abc"))
就这代码怎么把里面的 search-forward
搬到其它线程去?
case-fold-search
的作用域怎么处理?
比如说:
(let ((team (make-team 1)))
(make-thread (lambda ()
(%with-held-buffer-lock
(let ((case-fold-search nil))
(search-forward "abc"))))
nil team))
现有elisp运行环境的全局资源太不安全了
没关系,要求是 C 端不会崩溃就行了
剩下的可以由 Lisp 端的 mutex 保护
而现在我遇到的难题是在 GC 过程中如何停下其他线程。我正在尝试对其他线程发送 SIGUSR1,在 SIGUSR1 的 handler 中运行 __builtin_unwind_init 和等待 _gc_cvar condition variable,不知道这样可不可行
这么来说也挺容易, 可以在现有emacs基础上, 用lsp类似的rpc通信模式来实现, 外部启动一个node.js进程, 两边定义一套rpc协议, 然后就可以在这个基础上搞很多应用了, emacs负责派发(繁重的)任务给node.js, node.js负责执行并产生结果, 然后emacs接收结果并展示.
类似lsp, 也部分类似EAF.
这样不单可以外挂node.js, 还可以外挂其他任何运行环境, 比如python, lua等等, 甚至可以跟neovim打通.
跟界面有关的, 比如锁住一个buffer, 有可能卡住界面. 另外, 全局变量太多了, 几十上百个全局变量, 怎么锁? 读写都要锁啊
这个应用有点高级了, 还想象不出来使用场景, 有点像浏览器
EAF 其实就是一个IPC设计,Elisp可以调用Python、NodeJS和C/C++联合辅助Emacs分担计算压力。
Elisp自己多线程的问题是:
- 全局状态、Hook太多,一旦产生多线程写冲突,出错太多就没人想写多线程代码
- 不是靠通讯而是靠锁的机制,锁多了,各种相互卡,死锁就会导致没人用
多线程最佳模型就是不要用锁,独立线程间用队列通讯,同时图形代码和非图形代码一定要分开调用,Elisp这么多API, 根本无法完全区分开来。
现在Emacs理论上有线程这些东西,但是多线程编程模型完全没法用,不是技术问题,是设计问题。
现在就是emacs的展示能力有点差, 所以EAF是自己展示了
沙箱也不行,因为Emacs本身就是一个解释器,状态在发生变化(比如已加载符号和文件等),比如要写一个查询所有命令的插件,fork一个干净的沙箱就没有这么多已加载的列表,所以沙箱没有状态也不行。
现在最合理的方式就是,Emacs这么多年大杂烩超动态hook集合的模式,就让他这样,没有多线程只要小心维护文本编辑性能足够的,其他图形和耗时计算用外部进程或者EAF类似技术分离出去,用正规军的打法去实现,不要到处再 hacking way 了。
沙箱里面功能是受限的, 你说的命令列表是emacs主线程的吧? 沙箱里面不能访问这种外部资源. 每个沙箱环境是独立的, 类似于一个独立emacs进程的环境. 沙箱就是省掉了创建emacs进程的开销, 把它放线程里了.