全局变量 / 任务变量 / 锁

SPL 变量有三种作用域:局部变量、全局变量、任务变量。默认变量都是局部变量,作用域在同一个 SPL 脚本的范围内(不含主子线程代码),用法比较简单,本文不涉及。下面重点讲另两种变量,以及与之相关的锁。

全局变量

SPL 中全局变量的作用域是同一个 JVM(计算节点 \SPL 服务)的生命周期,在这个 JVM 运行的所有 SPL 脚本中都能访问到。

全局变量使用 env 函数赋值:

>env(gv1,1) //gv1 是全局变量名:

在脚本中直接用变量名访问就可以了

=gv1+2 // 等于 3

也可以删除:

>env(gv1)

有些数据可能会被多个脚本共享,在内存容量许可时预加载进内存,避免每次临时读取,从而提高性能。

服务启动时在 init.splx 加载全局变量。


A B
1 =connect("orcl") 连接数据库
2 =A1.query@x("select OrderID,Client,SellerID,OrderDate,Amount from orders order by OrderID")
3 =A2.index() 建立索引
4 >env(orders,A3) 全局变量赋值

在脚本中就可以直接使用它


A B
1 =orders.select(OrderDate>=arg1 && OrderDate<arg2) 直接引用全局变量
2 =A1.groups(Client; sum(Amount):s,count(1):c)

任务变量

有时一个复杂任务可能需要多个脚本共同配合才能完成,这些脚本可能会共享一些信息,总是通过脚本的参数和返回值来传递会比较麻烦,简单的想法就可以使用全局变量来实现。

但是,一个服务器可能同时并发很多任务,如果使用全局变量,会造成这个变量对所有任务共享,这常常不是我们想要的。

为此,SPL 提供了任务变量的机制。

一个 SPL 脚本(包括无脚本的 SPL 代码)发起,通过 call 函数执行的多个脚本完成的一次计算称为一个任务。同一个 JDBC 的连接,由多个 SPL 脚本共同完成的一次计算,也认定为一个任务。

任务变量的用法和全局变量类似,赋值时加一个选项 @j 即可:

>env@j(jv1,1) //jv1 是任务变量名

在脚本中直接用变量名访问:

=jv1+2 // 等于 3

也可以删除:

>env@j(jv1)

比如,主脚本将作为参数的用户名设置为任务变量 userID,子脚本使用 userID 进行计算。不同的任务(以不同参数执行的主脚本)下,userID 的值会不一样,互相不会影响。

主脚本


A B
1
2 >env@j(userID, arg) 将 userID 设为任务变量
3 =call("sub.splx") 子脚本
4

sub.splx:


A B
1 =connect("orcl")
2 =A1.query@x("select deptID,deptName from account where userID=?",userID) 使用任务变量
3

多个线程同时读写一个共享资源时,会导致不可预知的结果,比如某一个线程将共享变量从 1 修改成 2 再加 3,预期得到结果 5,但在执行加 3 的前一刻,另一个线程将该变量修改成了 4,此时前一个脚本继续将该变量加 3,将得到错误的结果 7。这种情况下可以用锁来保证并发读写的正确性,某个脚本对共享资源进行写操作前可以先上锁,其他脚本如果此时试图读写该资源,就必须等待前一个脚本解锁该资源,或者等待一段时间后放弃读写该资源。

比如,全局变量 m 用来记录某个文件被访问的次数,该文件会被多个脚本并发访问,为了避免读写冲突,应当使用锁。


A B
1 =file("d:/Orders.csv") 访问文件
2 >lock(10) 上锁,锁名是 10
3 =m=m+1 访问次数加 1
3 >lock@u(10) 解锁
4 =A2.import@t().select(Amount>arg1 && Amount<arg2)

脚本被同时执行时候(假设 2 线程并发,m 初值 =0),如果不使用锁,两个线程里的 m+1 可能同时执行,都是 0+1=1,最终结果是 1,结果显然错误;使用锁之后,两个线程里的 m+1 必须分别执行,先上锁的先执行,0+1=1,前者释放资源后后者再执行,此时 m=1,所以 m+1=2,得到的结果正确。有时候前一个线程迟迟没有释放资源(甚至发生死锁),本线程不能无限等待,这时就要使用函数 lock 的第 2 个参数超时时间,意即本线程最多等待 N 毫秒,如果之前锁住资源的其他线程在这期间解锁了,函数 lock 将返回锁名,本线程应当正常锁住该资源并使用;如果其他线程超过 N 毫秒也没解锁,函数 lock 将返回 0,本线程应当进行意外处理。