You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程下SBCL全局哈希表gethash返回NIL问题排查

SBCL多线程环境下全局哈希表gethash返回NIL的问题排查

问题现象

单线程模式下dictionary-compatible函数正常工作,但使用lparallel库的多线程新镜像时,对全局哈希表*lci-hash-table*的gethash调用始终返回nil。该哈希表仅用于读取,未做更新操作,按SBCL手册说明无需设置:synchronized或用sb-ext:defglobal定义。

核心代码

全局哈希表定义与核心函数

(defparameter *lci-hash-table* (make-hash-table :size 10000))  
  "Hash-table key = length|char|index concatenated together (eg, 5A0),
   hash-table values = list of matching symbols (eg, (ABATE ABBEY ...))."

(defun dictionary-compatible ($new-cross-str)  ;eg, "A     " of length 5
  "Tests if a string (with uppercase alphabetic and space characters)
   is compatible with the dictionary words (symbols) in a hash-table."
  (iter (with len = (length $new-cross-str))
        (for char in-sequence $new-cross-str)
        (for index from 0)
        (when (not (eql char #\Space))
          (collect (gethash (ut::intern-symbol len char index)  ;gethash始终返回nil
                            *lci-hash-table*) into dict-words))
        (finally (return (reduce #'intersection dict-words)))))

(defun intern-symbol (&rest args)
  "Interns a symbol created by concatenating args.
   Based on symb in Let Over Lambda."
  (flet ((mkstr (&rest args)
           (with-output-to-string (s)
             (dolist (a args) (princ a s)))))
    (values (intern (apply #'mkstr args)))))

哈希表填充代码

(defun create-lci-ht (dictionary-file)
  "Read dictionary words (symbols) from a file into strings
   and store in a lci hash table."
  (with-open-file (infile dictionary-file :direction :input :if-does-not-exist nil)
    (when (not (streamp infile)) (error "File does not exist!"))
    (with-open-file (stream dictionary-file)
      (let ((word-strings (uiop:read-file-lines stream)))
        (iter (for word-string in word-strings)
              (for word-length = (length word-string))
              (while word-string)
              (iter (for char in-sequence word-string)
                    (for index from 0)
                    (push (intern word-string)
                          (gethash (ut::intern-symbol word-length char index)
                                   *lci-hash-table*)))))))

问题根源

问题出在intern-symbol函数中的intern调用:该函数未指定固定的目标包,而是依赖于调用时的动态*package*变量。

  • 填充哈希表时,若调用环境的当前包是UT,则intern会将生成的键符号存入UT包(如UT:5A0)。
  • 多线程worker的运行环境中,当前包可能是默认的CL-USER,此时调用ut::intern-symbol时,内部的intern会将符号存入CL-USER包(如CL-USER:5A0)。

这两个符号属于不同包,即使名称相同,eql比较也会返回nil,导致gethash无法匹配到哈希表中的键,最终返回nil。

解决方案

修改intern-symbol函数,指定将符号intern到固定包(比如UT包),确保无论调用环境的当前包是什么,生成的键符号都一致:

(defun intern-symbol (&rest args)
  "Interns a symbol created by concatenating args into the UT package.
   Based on symb in Let Over Lambda."
  (flet ((mkstr (&rest args)
           (with-output-to-string (s)
             (dolist (a args) (princ a s)))))
    (values (intern (apply #'mkstr args) (find-package 'ut)))))

补充说明:也可以提前创建一个专门用于存储哈希表键的包,避免与其他包的符号冲突,原理是一致的。


内容的提问来源于stack exchange,提问作者davypough

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:07:45