全局变量 / 任务变量 / 锁

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,本线程应当进行意外处理。