首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >lisp中函数在执行过程中的重新定义

lisp中函数在执行过程中的重新定义
EN

Stack Overflow用户
提问于 2019-03-29 11:12:46
回答 1查看 98关注 0票数 1

假设我们有两个函数fct1fct2

  • fct1打电话给fct2
  • fct1将应用程序中的某些对象O1设置为状态A
  • fct2将应用程序中的某些对象O2设置为状态B

让我们假设以下约束必须始终都是正确的:

  • (01处于A状态,02处于B状态),
  • XOR (01处于not(A)状态,02处于not(B)状态)。

如果,在调用fct1时会发生什么情况:

  • 重新定义:fct1现在将一些对象01设置为状态not(A)
  • 重新定义:fct2现在将一些对象02设置为状态not(B)

它能否通过将01设置为状态A,将02设置为状态not(B)来“打破”约束?

我找到了这个答案:https://stackoverflow.com/a/20477763/9614866

如果递归函数重新定义了自身,那么仍然要在同一调用中执行的递归调用可能会继续传递到同一个实体。..。更普遍的是,Common允许编译器在同一个文件中的函数之间生成高效的调用。因此,通常您必须认为替换运行中的代码是在模块级别,而不是单个函数级别。如果函数A和B位于同一个模块中,而A调用B,那么如果您只是替换B而不替换A,A可以继续调用旧B(因为B被内联到A中,或者因为A没有经过符号,而是对B使用了更直接的地址)。您可以声明函数notinline来抑制这种情况。

我的问题是:

  • 这一现象是否会发生(即01设置为A状态,02设置为not(B)状态)?有名字吗?

如“是”:

  • 它依赖于实现吗?
  • 是否有办法强制正确的行为,例如通过内联一个函数?
  • 我可以用什么工具来测试函数的工作方式是否正确?测试似乎很痛苦:我不知道如何在不改变基本源代码的情况下测试重新定义。
  • 如何检测代码中可能出现此问题的部分?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-29 13:25:38

下面是您描述的在多线程环境中如何发生的例子:

代码语言:javascript
复制
(progn

  (defun f2 (o2)
    (setf (car o2) :b))

  (defun f1 (o1 o2)
    (setf (car o1) :a)
    ;; the sleep here is to increase the risk of data-race
    (sleep 3)
    (f2 o2))

  ;; call the functions in a separate thread
  (sb-thread:make-thread
   (lambda () 
     (let ((o1 (list 0))
           (o2 (list 0)))
       (f1 o1 o2)
       (print (list o1 o2)))))

  ;; in parallel, redefine f2, then f1
  (defun f2 (o2)
    (setf (car o2) :not-b))

  (defun f1 (o1 o2)
    (setf (car o1) :not-a)
    (f2 o2)))

3秒后,REPL打印

代码语言:javascript
复制
((:A) (:NOT-B))

如果在定义(declaim (inline f2))之前添加f2并再次进行测试,那么来自旧f2的代码仍将在线程中从旧f1执行,该线程在3秒后打印如下代码:

代码语言:javascript
复制
((:A) (:B)) 

对更新后的函数f1的进一步调用提供:

代码语言:javascript
复制
((:NOT-A) (:NOT-B)) 

我可以用什么工具来测试函数的工作方式是否正确?测试似乎很痛苦:我不知道如何在不改变基本源代码的情况下测试重新定义。

也许您正在用新代码更新正在运行的服务器,并且希望避免服务器在加载定义时使用函数的部分重新定义。

与基础设施的其他各个方面一样,重要的是提前计划如何可靠地进行备份和更新(数据库、配置等)。

一种可能的方法是逐包更新事物。您可以用版本号作为包的后缀:

代码语言:javascript
复制
(in-package :web-0 ...)
(defun f2 () ...)
(defun f1 () ...)

;; new version
(in-package :web-1 ...)
(defun f2 () ...)
(defun f1 () ...)

当需要更新时,您可以编译和加载:web-1的代码,而不会干扰正在运行的代码。然后,您应该能够更改调用方以使用新的实现:

代码语言:javascript
复制
;; somewhere in the main server package
(handle-request (request)
   (web-0:handle request))

 ;; update it
(handle-request (request)
  (web-1:handle request))

它甚至应该在没有版本号的情况下工作,如果您首先删除包,然后用相同的名称重新创建它,那么您将很难恢复。

有些地方可能还需要一个全局锁,您必须在应用程序中和更新期间管理该锁。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55416205

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档