我想知道 DWScript 是否能够在脚本中使用线程,因为某些引擎不会同步对其内部数据结构的访问。
2 回答
Arnaud 给出了关键点:
- 每个编译器实例一次只能从一个线程调用。您可以在多个线程中同时调用多个编译器实例,并且可以从多个线程中使用特定的编译器实例,前提是一次只有一个线程使用它。
- 每个编译的程序可以有多次执行,每次执行都可以在自己的线程中运行。此外,一个特定的执行可以被多个线程使用,前提是一次只有一个线程使用它。
- 每个执行都有自己的变量空间,它自己的堆栈,对象实例在堆上,并且在技术上可以在执行之间共享,没有锁定机制,但你可以自己制作。
- 脚本引擎在使用暴露给它的类或函数(通过 TdwsUnit、RTTI 等)时不会执行任何同步或锁定,因此在线程中运行脚本执行时,请确保只暴露线程安全的东西(特别是请注意 RTTI,因为很多 RTL 和 VCL 一开始就不是线程安全的)
运行脚本的多次执行类似于在 Delphi 中拥有多个线程,尽管每个新的执行不仅有自己的堆栈(如 Delphi 线程)而且还有自己的变量空间(在 Delphi 中有点像如果你有“线程var”无处不在)。并且 DWScript 执行不必在它们自己的线程中,它们可以跨线程移动,或轮询并在较少数量的线程中使用(唯一的限制是每次执行一次只能由一个线程使用,如上文提到的)。
因此,没有什么可以阻止您在脚本函数中公开一个会产生线程(和相应的执行)的函数,但是跨执行的通信不会通过共享变量(就像您可能会在 Delphi 中尝试做的那样),但会通过您自己的公开函数(或外部变量)、返回值(使用“评估”方法,参见单元测试)、“共享”对象实例或“全局变量”。
“全局变量”是指在 dwsGlobalVarsFunctions.pas 中定义的函数,可用于执行间数据交换。要激活它们,只需在项目中的某处使用“使用 dwsGlobalVarsFunctions”。
它们公开了一组 Read/WriteGlobalVar 函数,允许在同一个 Delphi 进程中运行的所有脚本执行中存储和检索命名变量,并且从线程的角度来看,这些读取和写入是“原子的”。
甚至没有必要打开 DWS 文档。:)
例如,[DWS] 现在能够对单个编译脚本执行多个线程安全的执行,而旧代码库是围绕编译脚本一次只能由一个线程执行的限制而构建的。
简而言之:
- DWS 编译器不是线程安全的:您必须在一个线程中创建执行堆栈(您不能共享一个编译器实例,每个编译器实例需要一个线程);
- DWS 执行是线程安全的,如果每个线程使用一个执行实例:您可以在多个线程中运行相同的编译脚本;
- 线程之间的通信不可用 AFAIK,但如果您需要同步,您可以使用 Delphi 代码。
当然,这里是关于 DWS 中线程安全的官方文档页面。
您现在可以根据需要为给定的 IdwsProgram 执行任意数量的程序,每次执行将仅将内存用于其堆和堆栈,已编译的表达式树是共享的。两个新接口都使用引用计数内存管理。