如何在Common Lisp中实现表单并行执行?类似并行PROGN的方案
解决方案
你的需求本质是并行执行一系列无依赖、无共享修改冲突的独立语句,lparallel完全可以通过「将每个赋值操作包装为匿名函数,批量调度执行」来实现,具体步骤如下:
1. 初始化线程池(可选,首次使用时执行)
先根据你的CPU核心数创建线程池,后续所有并行任务都会在这里调度:
(lparallel:make-kernel 4) ; 示例:创建4个工作线程,可根据硬件调整
2. 包装赋值语句为任务函数
将每一条incf语句封装成无参匿名函数,因为formula是编译时生成的表达式,这些lambda会在编译阶段直接展开对应的计算逻辑,不会引入额外运行时开销:
;; 假设原语句是这些 (incf (aref res 1) formula1) (incf (aref res 2) formula2) (incf (aref res 3) formula3) ;; 包装为任务列表 (defparameter *parallel-tasks* (list (lambda () (incf (aref res 1) formula1)) (lambda () (incf (aref res 2) formula2)) (lambda () (incf (aref res 3) formula3)) ;; ... 追加所有其他赋值任务 ))
3. 并行执行所有任务
使用lparallel提供的批量任务执行函数,直接调度所有任务到线程池并行运行:
方法一:用pmapcar(通用方式)
通过funcall调用每个任务函数,pmapcar会自动分配到不同线程执行,直到所有任务完成:
(lparallel:pmapcar #'funcall *parallel-tasks*)
方法二:用run-tasks(更贴合无返回值场景)
如果不需要任务的返回值,run-tasks是更轻量的选择,专门用于执行无返回的并行任务:
(lparallel:run-tasks *parallel-tasks*)
关键注意事项
- 因为每个数组索引仅被一个任务修改,不存在多线程竞争,所以不需要额外加锁,完全线程安全。
- 线程池只需初始化一次,后续可以重复使用;如果需要关闭线程池,执行
(lparallel:end-kernel)即可。 - 这种方式完全适配编译时生成的formula表达式,lambda会在编译阶段将formula的逻辑内联,不会影响计算效率。
内容的提问来源于stack exchange,提问作者MonadMania
相关产品推荐
相关产品推荐

