锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 / 线程的安全性
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

线程的安全性

一个函数可以被同一进程下的所有线程同时调用,并且该函数内调用的函数也可以被所 有线程同时调用时,那么这个函数就具备线程安全性(threadsafe)。

根据测试,绝大部分自已写的程序,无论是PGM,还是MODULE,都不具备线程的安全性,也就是不能被同一进程下的多个线程同时调用。

1 存储用法和线程应用

当我们定义了变量之后,多个线程就可能会访问或使用这些变量。应用程序中常用到的这些变量(的存储空间),以及其作用范围如下:

Global storage 全局变量(的存储空间):

在一个源文件或Module 中定义的全局变量,对该应用程序中其它的源文件、Module 都是可见的。共享范围为整个应用程序。这种共享是一个很常见的线程安全问题。(也就是在多线程并发时,要特别注意这类变量的赋值)

Static storage 静态变量(的存储空间)

静态变量也是全局变量的一种,只不过它的作用范围被限制在声明该变量的源文件、Module、或函数中。共享范围为声明静态变量的可执行Module 中。

Heap storage 动态分配的存储空间

这类存储由我们的应用程序动态进行控制,分配、回收存储空间,比如说 C 语言里的malloc(), free()函数。共享范围为整个应用程序。

如果在动态分配了存储空间之后,我们向另一个线程传递了该存储空间的指针(比如通过全局变量,或静态变量来传递,或者是其它的方法传递), 那么另一个线程就可以通过这个指针,来使用或回收这个存储空间。这种共享处理也是一个很常见的线程安全问题。

Automatic storage 自动分配的存储空间

函数内部私有变量,由系统自动分配存储空间。自动分配的存储空间对该进程下的其它线程是不可见的。每次线程调用函数的时候,都会重新自动分配存储空间。每个线程都拥有它们自己的自动分配的存储空间。基于复杂的串行、同步机制,一个线程不能访问另一个线程的自动分配的存储空间.

操作系统在活动作业组中,进一步限制了全局变量、静态变量、Heap 存储的作业范围。这意味着在不同的活动作业组中,使用了相同的全局变量、静态变量的程序,实际上访问的是这些变量和存储空间的不同版本,与此相似,尽管一个活动作业组可以使用另一个活动作业组中分配的 Heap 存储空间,但是不能回收Heap 存储空间。(也就是使用的其实是不同的版本)

2 JOB 级的资源

在编写多进程程序的时候,我们需要考虑 JOB 级的资源,当线程使用到这些资源的时候,必须不能与该进程下其它线程冲突。

有些资源的作业范围是活动作业组的级别,比如说打开数据库文件。我们编写多进程程序时,需要把这种活动作业组级别的资源也视为JOB 级的资源来使用。

如果一个线程在使用这些资源,而另一个线程需要修改这些资源时,我们的应用程序就需要考虑使用合适的同步机制来处理这类问题。

常见的 JOB 级,活动作业组级的资源有以下几种,在使用时要注意线程间的冲突:

动态分配的存储空间、静态变量、全局变量

这是最常见的共享资源,

打开的文件(Open Files)

当我们打开了一个文件之后,同一进程下的所有线程就可以共享文件系统的文件,以及数据库文件,这种共享可以通过线程之间传递指针或文件描述符来实现。

工作目录总是进程级的共享。(Scope of process)

CCSID、环境变量

CCSID 与环境变量都是JOB 级的资源。更改这两类资源,将会影响到该JOB 下的所有线程。

3 API 的线程安全级别

每一个 API 函数都有一个线程安全级别。在使用这些API 之前,我们需要确认在多线程中调用的API 是否足够安全。

API 的线程安全级别分为以下几类:

安全(yes)

这类 API 函数可以在并发的多个线程中安全地同时调用,而不需要进行任何限制。这种类型的API 内部所调用的函数,也具体线程安全性。

一定条件下安全(Conditional)

这个标志说明这类 API 函数所提供的功能,有一些是不具体线程安全性的。在API 函数的说明中,会指出线程安全的限制条件。(即哪些条件下调用API 函数,线程是安全的)

这类 API 函数的产生有可能因为系统底层支持不具备线程安合性,也可能是因为API函数会调用一个退出指针?(API can call a exit point)

举例而言,许多文件系统的API 函数在一个具体线程安全性的文件系统中使用文件,是完全安全的。但是一些在一定条件下安全的API 函数,在相同的环境中,就有可能拒绝访问。在API 函数说明中,将会说明在哪些条件下,函数会拒绝访问。

不安全(No)

这类 API 函数不具备线程安全性,本来不应该在多线程程序中使用。有时,这类API函数可能会拒绝访问,有时也不会(大部分函数都不会拒绝访问)。

与 CL 命令不同,当调用不安全的API 函数时,系统不会在JOB LOG 中产生调试信息。(也就是说CL 命令调用这些不安全的API 函数时,会JOBLOG 中生成调试信息)

在多线程中使用不具备线程安全性的 API 函数需要一定的技巧。

4 CL 命令和线程安全

ILE 环境下的CL 命令,或是编译后的CL 程序,是具备线程安全性的;

原始程序模型(OPM)下的CL 程序不具备线程安全性。

OPM 环境下的CL 代码,或者是4.3 以前的版本中ILE 环境下的CL 代码,在CL 命令执行时会发送一个CPD000B 的调试信息,接下来继续执行命令(该执行的结果未知)。这可能会导致线程的不安全,也可能不会,取决于底层代码的支持。

对于一个命令来说,与线程安全相关的有两个参数:

线程安全属性(threadsafe attribute – THDSAFE)

多线程作业运行属性(multithreaded job action attribute – MLTTHDACN)

多线程作业运行属性仅针对不具备线程安全性的命令(即线程安全属性为NO 时才有效),系统对多线程作业运行属性设置不同参数时的处理如下:

*NORUN

系统先发一个CPD000D 的调试信息,然后不执行这个命令。在发送CPD000D 之后,将会再发送一个CPF0001 的退出信息。

*MSG

系统同样先发送一个 CPD000D 的调试信息,然后开始执行这个命令

*RUN

系统不发送调试信息,直接开始运行。

如果一个 JOB 支持多线程,但并没有使用多线程时,系统也允许不具备线程安全性的程序直接运行。

当我们使用 DSPCMD 命令查看该命令的多线程作业属性时,有时系统显示的值为*SYSVAL,也就是系统默认值。这时可以使用DSPSYSVAL 来做进一步查看:

DSPSYSVAL SYSVAL(QMLTTHDACN)

也可以用CHGSYSVAL 来修改这个系统值(不过一般开发人员好象没有权限更改系统值)

5 拒绝访问的函数和线程安全

基于系统完整性的考虑,以及为防止数据的毁坏,有些API 函数以及CMD 命令在一定条件下具备线程安全性,而在某些条件下则不具备线程安全性。这些API 以及命令将有可能会拒绝部分或所有的访问。

拒绝访问的分类条件如下:

多线程能力

在这种情况下,函数是否拒绝访问,取决于当前 JOB 对多线程的支持能力。如果当前JOB 支持多线程,但并不关注当前JOB 中线程的数量时,那我们就不能调用这类函数。此时,函数会返回一个CPF1892 的退出信息给调用者。

初始线程

有些函数只能在初始线程中被调用。如果我们在辅线程中调用这类函数,函数会返回一个CPF180C 的的退出信息给调用者。如果要在辅线程中使用的话,可以通过向初始线程发出一个请求,然后初始线程调用的方式来实现这个功能。OVRDBF 就是一个常见的只能在初始线程中调用的例子。

多个线程(More than on thread)

在这种情况下,JOB 中的线程数量将会导致函数拒绝访问。如果JOB 中超过了一个线程,那么函数将会返一个CPF180B 的退出信息给调用者。其它函数将会在返回错误信息时将其赋值为ENOSAFE(3524).

这些在多线程下可能会拒绝访问的函数,在整个 JOB 中只有一个线程运行时,是不会拒绝访问的,可以随时调用。

所有访问文件系统的 API 函数,都不具备线程安全性。

友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州大学北校区院(文化路97号院)内