【问题标题】:Is DWScript thread-safe?DWScript 线程安全吗?
【发布时间】:2012-06-02 09:44:56
【问题描述】:

我想知道 DWScript 是否能够在脚本中使用线程,因为某些引擎不会同步对其内部数据结构的访问。

【问题讨论】:

  • -1 通过阅读文档、DWScript 项目网站的首页、网络搜索等,这个问题得到了简单的回答。在问问题之前要努力。
  • 如果答案这么容易找到,那肯定是跳过了好几次,否则我不会发布这个问题。
  • @david:如果每个人都只是 rtfm,就不会有 stackoverflow :)
  • 你的问题很难猜。 “在脚本中使用线程”并不明显。您是指编译、运行时上下文还是在 ONE 脚本中(没有 S)。越明确越好。例如,您可能已经谈到“TThread 的 DWS 等价物是什么?”。正如我的回答中所述,您将得到确切的答案:什么都没有 - DWS 脚本是单线程执行堆栈。

标签: delphi dwscript thread-safety


【解决方案1】:

Arnaud 给出了重点:

  • 每个编译器实例一次只能从一个线程调用。您可以同时在多个线程中调用多个编译器实例,并且可以从多个线程中使用特定的编译器实例,前提是一次只有一个线程使用它。
  • 每个编译的程序可以有多次执行,每次执行都可以在自己的线程中运行。此外,一个特定的执行可以由多个线程使用,前提是一次只有一个线程使用它。
  • 每个执行都有自己的变量空间,以及自己的堆栈,对象实例位于堆上,并且在技术上可以跨执行共享,没有锁定机制,但您可以自己创建。
  • 当使用暴露给它的类或函数(通过 TdwsUnit、RTTI 等)时,脚本引擎不会执行任何同步或锁定,因此在线程中运行脚本执行时,请确保只暴露线程安全的东西(对于 RTTI,要特别小心,因为很多 RTL 和 VCL 从一开始就不是线程安全的)

运行脚本的多次执行类似于在 Delphi 中拥有多个线程,尽管每个新的执行不仅有自己的堆栈(如 Delphi 线程)而且还有自己的变量空间(在 Delphi 中有点像如果你到处都有“线程变量”)。并且 DWScript 执行不必在它们自己的线程中,它们可以跨线程移动,或者轮询并在较少数量的线程中使用(唯一的限制是每次执行一次只能由一个线程使用,如上面提到的)。

所以没有什么可以阻止您在脚本函数中公开一个会产生线程(和相应执行)的函数,但是跨执行的通信不会通过共享变量(就像您在 Delphi 中可能会做的那样),但必须通过您自己的公开函数(或外部变量)、返回值(使用“评估”方法,参见单元测试)、“共享”对象实例或“全局变量”。

“全局变量”是指在 dwsGlobalVarsFunctions.pas 中定义的函数,可用于执行间数据交换。要激活它们,只需在项目中的某处添加“使用 dwsGlobalVarsFunctions”即可。

它们公开了一组 Read/WriteGlobalVar 函数,允许在同一个 Delphi 进程中运行的所有脚本执行中存储和检索命名变体,从线程的角度来看,这些读取和写入是“原子的”。

【讨论】:

  • 很高兴在 SO 中有 DWS“教父”(或新爸爸)! :)
  • 当一个线程正在执行一个TdwsProgramExecution对象时,另一个线程可以安全地调用TdwsProgramExecution.EndProgram吗?
  • 否,但您可以为此调用 Stop(如果您通过 Execute() 执行,而不是使用 BeginProgram/RunProgram 手动执行,这也有效)。这将导致脚本在其控制下尽早中止,即。如果您的脚本正在运行 Delphi 函数,它只会在从该函数返回后停止。 EndProgram 会进行清理,并且应该从正在执行的线程中调用。
【解决方案2】:

甚至不需要打开 DWS 文档。 :)

看看at this StackOverflow answer by Eric:

例如,[DWS] 现在能够以多个线程安全的方式执行单个编译脚本,而旧代码库是围绕编译脚本一次只能由一个线程执行的限制而构建的。

简而言之:

  • DWS 编译器不是线程安全的:您必须在一个线程中创建执行堆栈(您不能共享一个编译器实例,每个编译器实例需要一个线程);
  • DWS 执行是线程安全的,如果每个线程使用一个执行实例:您可以在多个线程中运行相同的编译脚本;
  • 线程之间的通信不可用 AFAIK,但如果需要同步,可以使用 Delphi 代码。

当然,这里是the official documentation page about thread safety in DWS

现在,您可以根据需要为给定的 IdwsProgram 执行任意数量的程序,每次执行将仅将内存用于其堆和堆栈,已编译的表达式树是共享的。两个新接口都使用引用计数内存管理。

【讨论】:

  • 通读后,我意识到我已经知道这一点,但除非我错了,否则它并不能完全回答我的问题......我不想在其中运行相同的脚本多个线程,我想在单个脚本中运行多个线程...
  • AFAIK 到目前为止,我了解执行类的实现,这是不可能的,设计使然。没有 TThread 之类的。唯一的可能性是在一个线程中为每个脚本运行多个脚本(当然这是可能的),然后尝试在脚本之间使用 IPC 或互斥锁,就像我在第三点中所写的那样:线程之间没有通信从头开始可用。我宁愿推荐一种无状态架构,其中一些共享数据结构由 Delphi 类提供(例如,使用 RTTI 暴露到 DWS 中)。
猜你喜欢
  • 2020-04-15
  • 2011-07-04
  • 2014-04-26
  • 2012-11-30
  • 2010-12-30
  • 2013-03-12
  • 2021-08-03
  • 2010-12-27
  • 2018-06-04
相关资源
最近更新 更多