【问题标题】:Multi-threaded (Parallel) Access to Built-in Common Lisp Objects对内置 Common Lisp 对象的多线程(并行)访问
【发布时间】:2019-03-26 20:13:05
【问题描述】:

多线程访问 Lisp 对象的话题在 https://stackoverflow.com/posts/comments/97440894?noredirect=1 的另一篇文章中提出,但作为一个附带问题,我希望得到进一步的澄清。

一般来说,Lisp 函数(以及特殊形式、宏等)似乎很自然地分为对象的访问器和修饰符。共享对象的修饰符在多线程应用程序中显然存在问题,因为同时发生的更新可能会相互干扰(需要保护锁、原子操作等)。

但潜在访问者干扰的问题似乎不太清楚。当然,可以编写任何访问器以包含潜在的修改代码,但我认为基本的 Lisp 访问器操作(如 CLHS 中指定并为各种平台实现的)不会。但是,出于效率的原因,我怀疑可能存在极少数例外情况——如果在没有保护的情况下在多线程代码中使用这些例外情况,则最好注意这些例外情况。 (我所说的异常类型是 notmaphash 这样的操作,它可以用作访问器和修饰符。)

如果任何具有实施经验的人都可以指出至少一个内置的仅访问操作(例如在 SBCL 或其他来源中),其中包括可能存在麻烦的修改,那将很有帮助。我知道保证很难得到,但启发式指导也很有用。

【问题讨论】:

  • 我不知道 Common Lisp,但是在其他语言中,如果另一个线程同时更新同一个对象,并且如果访问或更新更多,即使是纯“访问器”也会遇到麻烦不仅仅是一个单一的原子硬件操作。每当访问器和修改器之间存在数据竞争时,访问器可能会看到对象处于无效的、中途更改的状态。根据调用者对“访问”数据的处理方式,可能会发生任意的坏事(计算结果不正确、段错误、数据损坏等)。
  • '这会很有帮助'。那有什么用呢?你的实际问题是什么?
  • 您必须使用锁来保护列表在您遍历它时不被其他线程修改(无论您使用什么来遍历它,包括FIND)。 CLHS 3.6 禁止在遍历列表时修改其 cdr 链(这适用于所有“在遍历期间执行的代码”,因此即使标准中没有明确提及,也包括多线程)。一般来说,避免多线程修改问题的最简单方法是不做任何事情并使用不可变数据结构。
  • Hyperspec 有一个 FIND 文档:lispworks.com/documentation/HyperSpec/Body/f_find_.htm#find 它说“副作用:无”
  • 它还说如果在遍历过程中尝试修改序列会发生什么,这是未定义的。

标签: multithreading common-lisp concurrentmodification


【解决方案1】:

任何这样做的代码都将是支持多线程的实现中的错误。 SBCL 使用著名的*world-lock* 保护非线程安全的函数。

如果您有真正的理由需要不可变结构,请使用带有只读 defstruct 的 defconstant(defstruct number (value :read-only t))

(defconstant +five+ (make-number 5))

【讨论】:

  • 常量和不可变数据结构是不同的东西。不可变数据结构通常是您从不改变的列表或树,而是创建更改了某些部分的新树,尽可能多地共享结构。这对线程安全有好处,因为即使另一个线程同时在读取树,也不会发生任何不好的事情(它只是看不到更新的值)。另请注意,DEFCONSTANT 不应与结构一起使用,因为当新值不是旧值 EQL 时,重新评估表单是未定义的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-27
  • 2011-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
  • 2012-07-25
相关资源
最近更新 更多